• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2014> Wim Taymans <wim.taymans@gmail.com>
3  *
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.
8  *
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.
13  *
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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23 
24 #include <string.h>
25 #include <stdio.h>
26 #include <math.h>
27 
28 #include "video-resampler.h"
29 
30 #ifndef GST_DISABLE_GST_DEBUG
31 #define GST_CAT_DEFAULT ensure_debug_category()
32 static GstDebugCategory *
ensure_debug_category(void)33 ensure_debug_category (void)
34 {
35   static gsize cat_gonce = 0;
36 
37   if (g_once_init_enter (&cat_gonce)) {
38     gsize cat_done;
39 
40     cat_done = (gsize) _gst_debug_category_new ("video-resampler", 0,
41         "video-resampler object");
42 
43     g_once_init_leave (&cat_gonce, cat_done);
44   }
45 
46   return (GstDebugCategory *) cat_gonce;
47 }
48 #else
49 #define ensure_debug_category() /* NOOP */
50 #endif /* GST_DISABLE_GST_DEBUG */
51 
52 /**
53  * SECTION:gstvideoresampler
54  * @title: GstVideoResampler
55  * @short_description: Utility structure for resampler information
56  *
57  * #GstVideoResampler is a structure which holds the information
58  * required to perform various kinds of resampling filtering.
59  *
60  */
61 
62 
63 #define DEFAULT_OPT_CUBIC_B (1.0 / 3.0)
64 #define DEFAULT_OPT_CUBIC_C (1.0 / 3.0)
65 
66 #define DEFAULT_OPT_ENVELOPE 2.0
67 #define DEFAULT_OPT_SHARPNESS 1.0
68 #define DEFAULT_OPT_SHARPEN 0.0
69 
70 #define DEFAULT_OPT_MAX_TAPS 128
71 
72 typedef struct _ResamplerParams ResamplerParams;
73 
74 struct _ResamplerParams
75 {
76   GstVideoResamplerMethod method;
77   GstVideoResamplerFlags flags;
78 
79   gdouble shift;
80 
81     gdouble (*get_tap) (ResamplerParams * params, gint l, gint xi, gdouble x);
82 
83   /* for cubic */
84   gdouble b, c;
85   /* used by lanczos */
86   gdouble ex, fx, dx;
87   /* extra params */
88   gdouble envelope;
89   gdouble sharpness;
90   gdouble sharpen;
91 
92   GstVideoResampler *resampler;
93 };
94 
95 static gdouble
get_opt_double(GstStructure * options,const gchar * name,gdouble def)96 get_opt_double (GstStructure * options, const gchar * name, gdouble def)
97 {
98   gdouble res;
99   if (!options || !gst_structure_get_double (options, name, &res))
100     res = def;
101   return res;
102 }
103 
104 static gint
get_opt_int(GstStructure * options,const gchar * name,gint def)105 get_opt_int (GstStructure * options, const gchar * name, gint def)
106 {
107   gint res;
108   if (!options || !gst_structure_get_int (options, name, &res))
109     res = def;
110   return res;
111 }
112 
113 #define GET_OPT_CUBIC_B(options) get_opt_double(options, \
114     GST_VIDEO_RESAMPLER_OPT_CUBIC_B, DEFAULT_OPT_CUBIC_B)
115 #define GET_OPT_CUBIC_C(options) get_opt_double(options, \
116     GST_VIDEO_RESAMPLER_OPT_CUBIC_C, DEFAULT_OPT_CUBIC_C)
117 #define GET_OPT_ENVELOPE(options) get_opt_double(options, \
118     GST_VIDEO_RESAMPLER_OPT_ENVELOPE, DEFAULT_OPT_ENVELOPE)
119 #define GET_OPT_SHARPNESS(options) get_opt_double(options, \
120     GST_VIDEO_RESAMPLER_OPT_SHARPNESS, DEFAULT_OPT_SHARPNESS)
121 #define GET_OPT_SHARPEN(options) get_opt_double(options, \
122     GST_VIDEO_RESAMPLER_OPT_SHARPEN, DEFAULT_OPT_SHARPEN)
123 #define GET_OPT_MAX_TAPS(options) get_opt_int(options, \
124     GST_VIDEO_RESAMPLER_OPT_MAX_TAPS, DEFAULT_OPT_MAX_TAPS)
125 
126 static double
sinc(double x)127 sinc (double x)
128 {
129   if (x == 0)
130     return 1;
131 
132   return sin (G_PI * x) / (G_PI * x);
133 }
134 
135 static double
envelope(double x)136 envelope (double x)
137 {
138   if (x <= -1 || x >= 1)
139     return 0;
140   return sinc (x);
141 }
142 
143 static gdouble
get_nearest_tap(ResamplerParams * params,gint l,gint xi,gdouble x)144 get_nearest_tap (ResamplerParams * params, gint l, gint xi, gdouble x)
145 {
146   return 1.0;
147 }
148 
149 static gdouble
get_linear_tap(ResamplerParams * params,gint l,gint xi,gdouble x)150 get_linear_tap (ResamplerParams * params, gint l, gint xi, gdouble x)
151 {
152   gdouble res, a;
153   gint xl = xi + l;
154 
155   a = fabs (x - xl) * params->fx;
156 
157   if (a < 1.0)
158     res = 1.0 - a;
159   else
160     res = 0.0;
161 
162   return res;
163 }
164 
165 static gdouble
get_cubic_tap(ResamplerParams * params,gint l,gint xi,gdouble x)166 get_cubic_tap (ResamplerParams * params, gint l, gint xi, gdouble x)
167 {
168   gdouble a, a2, a3, b, c;
169   gint xl = xi + l;
170 
171   a = fabs (x - xl) * params->fx;
172   a2 = a * a;
173   a3 = a2 * a;
174 
175   b = params->b;
176   c = params->c;
177 
178   if (a <= 1.0)
179     return ((12.0 - 9.0 * b - 6.0 * c) * a3 +
180         (-18.0 + 12.0 * b + 6.0 * c) * a2 + (6.0 - 2.0 * b)) / 6.0;
181   else if (a <= 2.0)
182     return ((-b - 6.0 * c) * a3 +
183         (6.0 * b + 30.0 * c) * a2 +
184         (-12.0 * b - 48.0 * c) * a + (8.0 * b + 24.0 * c)) / 6.0;
185   else
186     return 0.0;
187 }
188 
189 static gdouble
get_sinc_tap(ResamplerParams * params,gint l,gint xi,gdouble x)190 get_sinc_tap (ResamplerParams * params, gint l, gint xi, gdouble x)
191 {
192   gint xl = xi + l;
193   return sinc ((x - xl) * params->fx);
194 }
195 
196 static gdouble
get_lanczos_tap(ResamplerParams * params,gint l,gint xi,gdouble x)197 get_lanczos_tap (ResamplerParams * params, gint l, gint xi, gdouble x)
198 {
199   gint xl = xi + l;
200   gdouble env = envelope ((x - xl) * params->ex);
201   return (sinc ((x - xl) * params->fx) - params->sharpen) * env;
202 }
203 
204 static void
resampler_calculate_taps(ResamplerParams * params)205 resampler_calculate_taps (ResamplerParams * params)
206 {
207   GstVideoResampler *resampler = params->resampler;
208   gint j;
209   guint32 *offset, *n_taps, *phase;
210   gint tap_offs;
211   gint max_taps;
212   gint in_size, out_size;
213   gdouble shift;
214   gdouble corr;
215 
216   in_size = resampler->in_size;
217   out_size = resampler->out_size;
218 
219   max_taps = resampler->max_taps;
220   tap_offs = (max_taps - 1) / 2;
221   corr = (max_taps == 1 ? 0.0 : 0.5);
222 
223   shift = params->shift;
224 
225   resampler->taps = g_malloc (sizeof (gdouble) * max_taps * out_size);
226   n_taps = resampler->n_taps = g_malloc (sizeof (guint32) * out_size);
227   offset = resampler->offset = g_malloc (sizeof (guint32) * out_size);
228   phase = resampler->phase = g_malloc (sizeof (guint32) * out_size);
229 
230   for (j = 0; j < out_size; j++) {
231     gdouble ox, x;
232     gint xi;
233     gint l;
234     gdouble weight;
235     gdouble *taps;
236 
237     /* center of the output pixel */
238     ox = (0.5 + (gdouble) j - shift) / out_size;
239     /* x is the source pixel to use, can be fractional */
240     x = ox * (gdouble) in_size - corr;
241     x = CLAMP (x, 0, in_size - 1);
242     /* this is the first source pixel to use */
243     xi = floor (x - tap_offs);
244 
245     offset[j] = xi;
246     phase[j] = j;
247     n_taps[j] = max_taps;
248     weight = 0;
249     taps = resampler->taps + j * max_taps;
250 
251     for (l = 0; l < max_taps; l++) {
252       taps[l] = params->get_tap (params, l, xi, x);
253       weight += taps[l];
254     }
255 
256     for (l = 0; l < max_taps; l++)
257       taps[l] /= weight;
258 
259     if (xi < 0) {
260       gint sh = -xi;
261 
262       for (l = 0; l < sh; l++) {
263         taps[sh] += taps[l];
264       }
265       for (l = 0; l < max_taps - sh; l++) {
266         taps[l] = taps[sh + l];
267       }
268       for (; l < max_taps; l++) {
269         taps[l] = 0;
270       }
271       offset[j] += sh;
272     }
273     if (xi > in_size - max_taps) {
274       gint sh = xi - (in_size - max_taps);
275 
276       for (l = 0; l < sh; l++) {
277         taps[max_taps - sh - 1] += taps[max_taps - sh + l];
278       }
279       for (l = 0; l < max_taps - sh; l++) {
280         taps[max_taps - 1 - l] = taps[max_taps - 1 - sh - l];
281       }
282       for (l = 0; l < sh; l++) {
283         taps[l] = 0;
284       }
285       offset[j] -= sh;
286     }
287   }
288 }
289 
290 static void
resampler_dump(GstVideoResampler * resampler)291 resampler_dump (GstVideoResampler * resampler)
292 {
293 #if 0
294   gint i, max_taps, out_size;
295 
296   out_size = resampler->out_size;
297   max_taps = resampler->max_taps;
298 
299   for (i = 0; i < out_size; i++) {
300     gint j, o, phase, n_taps;
301     gdouble sum;
302 
303     o = resampler->offset[i];
304     n_taps = resampler->n_taps[i];
305     phase = resampler->phase[i];
306 
307     printf ("%u: \t%d  ", i, o);
308     sum = 0;
309     for (j = 0; j < n_taps; j++) {
310       gdouble tap;
311       tap = resampler->taps[phase * max_taps + j];
312       printf ("\t%f ", tap);
313       sum += tap;
314     }
315     printf ("\t: sum %f\n", sum);
316   }
317 #endif
318 }
319 
320 
321 /**
322  * gst_video_resampler_new:
323  * @resampler: a #GstVideoResampler
324  * @method: a #GstVideoResamplerMethod
325  * @flags: #GstVideoResamplerFlags
326  * @n_phases: number of phases to use
327  * @n_taps: number of taps to use
328  * @in_size: number of source elements
329  * @out_size: number of destination elements
330  * @options: extra options
331  *
332  * Make a new resampler. @in_size source elements will
333  * be resampled to @out_size destination elements.
334  *
335  * @n_taps specifies the amount of elements to use from the source for one output
336  * element. If n_taps is 0, this function chooses a good value automatically based
337  * on the @method and @in_size/@out_size.
338  *
339  * Returns: %TRUE on success
340  *
341  * Since: 1.6
342  */
343 gboolean
gst_video_resampler_init(GstVideoResampler * resampler,GstVideoResamplerMethod method,GstVideoResamplerFlags flags,guint n_phases,guint n_taps,gdouble shift,guint in_size,guint out_size,GstStructure * options)344 gst_video_resampler_init (GstVideoResampler * resampler,
345     GstVideoResamplerMethod method, GstVideoResamplerFlags flags,
346     guint n_phases, guint n_taps, gdouble shift, guint in_size, guint out_size,
347     GstStructure * options)
348 {
349   ResamplerParams params;
350   gint max_taps;
351   gdouble scale_factor;
352 
353   g_return_val_if_fail (in_size != 0, FALSE);
354   g_return_val_if_fail (out_size != 0, FALSE);
355   g_return_val_if_fail (n_phases == out_size, FALSE);
356 
357   resampler->in_size = in_size;
358   resampler->out_size = out_size;
359   resampler->n_phases = n_phases;
360 
361   params.method = method;
362   params.flags = flags;
363   params.shift = shift;
364   params.resampler = resampler;
365 
366   GST_DEBUG ("%d %u  %u->%u", method, n_taps, in_size, out_size);
367 
368   params.sharpness = GET_OPT_SHARPNESS (options);
369   params.sharpen = GET_OPT_SHARPEN (options);
370 
371   scale_factor = in_size / (gdouble) out_size;
372   if (scale_factor > 1.0) {
373     params.fx = (1.0 / scale_factor) * params.sharpness;
374   } else {
375     params.fx = (1.0) * params.sharpness;
376   }
377 
378   max_taps = GET_OPT_MAX_TAPS (options);
379   n_taps = MIN (n_taps, max_taps);
380 
381   switch (method) {
382     case GST_VIDEO_RESAMPLER_METHOD_NEAREST:
383       params.envelope = GET_OPT_ENVELOPE (options);
384       params.get_tap = get_nearest_tap;
385       if (n_taps == 0)
386         n_taps = 1;
387       break;
388     case GST_VIDEO_RESAMPLER_METHOD_LINEAR:
389       params.get_tap = get_linear_tap;
390       params.envelope = 1.0;
391       break;
392     case GST_VIDEO_RESAMPLER_METHOD_CUBIC:
393       params.b = GET_OPT_CUBIC_B (options);
394       params.c = GET_OPT_CUBIC_C (options);
395       params.envelope = 2.0;
396       params.get_tap = get_cubic_tap;
397       break;
398     case GST_VIDEO_RESAMPLER_METHOD_SINC:
399       params.envelope = GET_OPT_ENVELOPE (options);
400       params.get_tap = get_sinc_tap;
401       break;
402     case GST_VIDEO_RESAMPLER_METHOD_LANCZOS:
403       params.envelope = GET_OPT_ENVELOPE (options);
404       params.get_tap = get_lanczos_tap;
405       break;
406     default:
407       break;
408   }
409 
410   if (n_taps == 0) {
411     params.dx = ceil (2.0 * params.envelope / params.fx);
412     n_taps = CLAMP (params.dx, 0, max_taps);
413   }
414   if (flags & GST_VIDEO_RESAMPLER_FLAG_HALF_TAPS && n_taps > 3)
415     n_taps /= 2;
416   params.fx = 2.0 * params.envelope / n_taps;
417   params.ex = 2.0 / n_taps;
418 
419   if (n_taps > in_size)
420     n_taps = in_size;
421 
422   resampler->max_taps = n_taps;
423 
424   resampler_calculate_taps (&params);
425 
426   resampler_dump (resampler);
427 
428   return TRUE;
429 }
430 
431 /**
432  * gst_video_resampler_clear:
433  * @resampler: a #GstVideoResampler
434  *
435  * Clear a previously initialized #GstVideoResampler @resampler.
436  *
437  * Since: 1.6
438  */
439 void
gst_video_resampler_clear(GstVideoResampler * resampler)440 gst_video_resampler_clear (GstVideoResampler * resampler)
441 {
442   g_return_if_fail (resampler != NULL);
443 
444   g_free (resampler->phase);
445   g_free (resampler->offset);
446   g_free (resampler->n_taps);
447   g_free (resampler->taps);
448 }
449