MythTV  master
filter_denoise3d.c
Go to the documentation of this file.
1 /* Spatial/temporal denoising filter shamelessly swiped from Mplayer
2  CPU detection borrowed from linearblend filter
3  Mplayer filter code Copyright (C) 2003 Daniel Moreno <comac@comac.darktech.org>
4  MythTV adaptation and MMX optimization by Andrew Mahone <andrewmahone@eml.cc>
5 */
6 
7 #include <stdlib.h>
8 #include <stdio.h>
9 
10 #include "mythconfig.h"
11 #if HAVE_STDINT_H
12 #include <stdint.h>
13 #endif
14 
15 #include <string.h>
16 #include <math.h>
17 
18 #include "filter.h"
19 #include "mythframe.h"
20 #include "../mm_arch.h"
21 
22 #define PARAM1_DEFAULT 4.0
23 #define PARAM2_DEFAULT 3.0
24 #define PARAM3_DEFAULT 6.0
25 
26 #define LowPass(Prev, Curr, Coef) ((Curr) + (Coef)[(Prev) - (Curr)])
27 
28 #undef ABS
29 #define ABS(A) ( (A) > 0 ? (A) : -(A) )
30 
31 #ifdef MMX
32 #include "ffmpeg-mmx.h"
33 static const mmx_t mz = { 0x0LL };
34 #endif
35 
36 typedef struct ThisFilter
37 {
39 
40  int offsets[3];
41  int pitches[3];
42  int mm_flags;
43  int line_size;
44  int prev_size;
45  uint8_t *line;
46  uint8_t *prev;
47  uint8_t coefs[4][512];
48 
49  void (*filtfunc)(uint8_t*, uint8_t*, uint8_t*,
50  int, int, const uint8_t*, const uint8_t*);
51 
52  TF_STRUCT;
53 } ThisFilter;
54 
55 static void calc_coefs(uint8_t * Ct, double Dist25)
56 {
57  double Gamma = log (0.25) / log (1.0 - Dist25 / 255.0);
58 
59  for (int i = -256; i <= 255; i++)
60  {
61  double Simil = 1.0 - ABS (i) / 255.0;
62  double C = pow (Simil, Gamma) * (double) i;
63  Ct[256 + i] = (C < 0) ? (C - 0.5) : (C + 0.5);
64  }
65 }
66 
67 static void denoise(uint8_t *Frame,
68  uint8_t *FramePrev,
69  uint8_t *Line,
70  int W, int H,
71  const uint8_t *Spatial, const uint8_t *Temporal)
72 {
73  uint8_t prev;
74  int X, Y;
75  uint8_t *LineCur = Frame;
76  uint8_t *LinePrev = FramePrev;
77 
78  prev = Line[0] = Frame[0];
79  Frame[0] = LowPass (FramePrev[0], Frame[1], Temporal);
80  for (X = 1; X < W; X++)
81  {
82  prev = LowPass (prev, Frame[X], Spatial);
83  Line[X] = prev;
84  FramePrev[X] = Frame[X] = LowPass (FramePrev[X], prev, Temporal);
85  }
86 
87  for (Y = 1; Y < H; Y++)
88  {
89  LineCur += W;
90  LinePrev += W;
91  prev = LineCur[0];
92  Line[0] = LowPass (Line[0], prev, Spatial);
93  LineCur[0] = LowPass (LinePrev[0], Line[0], Temporal);
94  for (X = 1; X < W; X++)
95  {
96  prev = LowPass (prev, LineCur[X], Spatial);
97  Line[X] = LowPass (Line[X], prev, Spatial);
98  LinePrev[X] = LineCur[X] = LowPass (LinePrev[X], Line[X], Temporal);
99  }
100  }
101 }
102 
103 #ifdef MMX
104 
105 static void denoiseMMX(uint8_t *Frame,
106  uint8_t *FramePrev,
107  uint8_t *Line,
108  int W, int H,
109  const uint8_t *Spatial, const uint8_t *Temporal)
110 {
111  int X, i;
112  uint8_t *LineCur = Frame;
113  uint8_t *LinePrev = FramePrev;
114  uint8_t *End = Frame + W * H;
115  int16_t wbuf[16];
116  uint8_t cbuf[16];
117 
118  Line[0] = LineCur[0];
119  for (X = 1; X < W; X++)
120  Line[X] = LowPass (Line[X-1], LineCur[X], Spatial);
121 
122  for (X = 0; X < W - 15; X += 16)
123  {
124  movq_m2r (LinePrev[X], mm0);
125  movq_m2r (LinePrev[X+8], mm2);
126  movq_m2r (Line[X], mm4);
127  movq_m2r (Line[X+8], mm6);
128  movq_r2r (mm0, mm1);
129  movq_r2r (mm2, mm3);
130  movq_r2r (mm4, mm5);
131  movq_r2r (mm6, mm7);
132 
133  punpcklbw_m2r(mz, mm0);
134  punpckhbw_m2r(mz, mm1);
135  punpcklbw_m2r(mz, mm2);
136  punpckhbw_m2r(mz, mm3);
137  punpcklbw_m2r(mz, mm4);
138  punpckhbw_m2r(mz, mm5);
139  punpcklbw_m2r(mz, mm6);
140  punpckhbw_m2r(mz, mm7);
141 
142  psubw_r2r (mm4, mm0);
143  psubw_r2r (mm5, mm1);
144  psubw_r2r (mm6, mm2);
145  psubw_r2r (mm7, mm3);
146 
147  movq_r2m (mm0, wbuf[0]);
148  movq_r2m (mm1, wbuf[4]);
149  movq_r2m (mm2, wbuf[8]);
150  movq_r2m (mm3, wbuf[12]);
151 
152  movq_m2r (Line[X], mm4);
153  movq_m2r (Line[X+8], mm6);
154 
155  for (i = 0; i < 16; i++)
156  cbuf[i] = Temporal[wbuf[i]];
157 
158  paddb_m2r (cbuf[0], mm4);
159  paddb_m2r (cbuf[8], mm6);
160  movq_r2m (mm4, LinePrev[X]);
161  movq_r2m (mm6, LinePrev[X+8]);
162  movq_r2m (mm4, LineCur[X]);
163  movq_r2m (mm6, LineCur[X+8]);
164  }
165 
166  for (/*X*/; X < W; X++)
167  LineCur[X] = Line[X] = LowPass (LinePrev[X], Line[X], Temporal);
168 
169  LineCur += W;
170  LinePrev += W;
171  while (LineCur < End)
172  {
173  for (X = 1; X < W; X++)
174  LineCur[X] = LowPass (LineCur[X-1], LineCur[X], Spatial);
175 
176  for (X = 0; X < W - 15; X += 16)
177  {
178  movq_m2r (Line[X], mm0);
179  movq_m2r (Line[X+8], mm2);
180  movq_m2r (LineCur[X], mm4);
181  movq_m2r (LineCur[X+8], mm6);
182  movq_r2r (mm0, mm1);
183  movq_r2r (mm2, mm3);
184  movq_r2r (mm4, mm5);
185  movq_r2r (mm6, mm7);
186 
187  punpcklbw_m2r(mz, mm0);
188  punpckhbw_m2r(mz, mm1);
189  punpcklbw_m2r(mz, mm2);
190  punpckhbw_m2r(mz, mm3);
191  punpcklbw_m2r(mz, mm4);
192  punpckhbw_m2r(mz, mm5);
193  punpcklbw_m2r(mz, mm6);
194  punpckhbw_m2r(mz, mm7);
195 
196  psubw_r2r (mm4, mm0);
197  psubw_r2r (mm5, mm1);
198  psubw_r2r (mm6, mm2);
199  psubw_r2r (mm7, mm3);
200 
201  movq_r2m (mm0, wbuf[0]);
202  movq_r2m (mm1, wbuf[4]);
203  movq_r2m (mm2, wbuf[8]);
204  movq_r2m (mm3, wbuf[12]);
205 
206  movq_m2r (LineCur[X], mm4);
207  movq_m2r (LineCur[X+8], mm6);
208 
209  for (i = 0; i < 16; i++)
210  cbuf[i] = Spatial[wbuf[i]];
211 
212  movq_m2r (LinePrev[X], mm0);
213  movq_m2r (LinePrev[X+8], mm2);
214  paddb_m2r (cbuf[0], mm4);
215  paddb_m2r (cbuf[8], mm6);
216  movq_r2m (mm4, Line[X]);
217  movq_r2m (mm6, Line[X+8]);
218 
219  movq_r2r (mm0, mm1);
220  movq_r2r (mm2, mm3);
221  movq_r2r (mm4, mm5);
222  movq_r2r (mm6, mm7);
223 
224  punpcklbw_m2r(mz, mm0);
225  punpckhbw_m2r(mz, mm1);
226  punpcklbw_m2r(mz, mm2);
227  punpckhbw_m2r(mz, mm3);
228  punpcklbw_m2r(mz, mm4);
229  punpckhbw_m2r(mz, mm5);
230  punpcklbw_m2r(mz, mm6);
231  punpckhbw_m2r(mz, mm7);
232 
233  psubw_r2r (mm4, mm0);
234  psubw_r2r (mm5, mm1);
235  psubw_r2r (mm6, mm2);
236  psubw_r2r (mm7, mm3);
237 
238  movq_r2m (mm0, wbuf[0]);
239  movq_r2m (mm1, wbuf[4]);
240  movq_r2m (mm2, wbuf[8]);
241  movq_r2m (mm3, wbuf[12]);
242 
243  movq_m2r (Line[X], mm4);
244  movq_m2r (Line[X+8], mm6);
245 
246  for (i = 0; i < 16; i++)
247  cbuf[i] = Temporal[wbuf[i]];
248 
249  paddb_m2r (cbuf[0], mm4);
250  paddb_m2r (cbuf[8], mm6);
251  movq_r2m (mm4, LinePrev[X]);
252  movq_r2m (mm6, LinePrev[X+8]);
253  movq_r2m (mm4, LineCur[X]);
254  movq_r2m (mm6, LineCur[X+8]);
255  }
256 
257  for (/*X*/; X < W; X++)
258  {
259  Line[X] = LowPass (Line[X], LineCur[X], Spatial);
260  LineCur[X] = LinePrev[X] = LowPass (LinePrev[X], Line[X], Temporal);
261  }
262 
263  LineCur += W;
264  LinePrev += W;
265  }
266 }
267 #endif /* MMX */
268 
269 static int alloc_line(ThisFilter *filter, int size)
270 {
271  if (filter->line_size >= size)
272  return 1;
273 
274  uint8_t *tmp = realloc(filter->line, size);
275  if (!tmp)
276  {
277  fprintf(stderr, "Couldn't allocate memory for line buffer\n");
278  return 0;
279  }
280 
281  filter->line = tmp;
282  filter->line_size = size;
283 
284  return 1;
285 }
286 
287 static int alloc_prev(ThisFilter *filter, int size)
288 {
289  if (filter->prev_size >= size)
290  return 1;
291 
292  uint8_t *tmp = realloc(filter->prev, size);
293  if (!tmp)
294  {
295  fprintf(stderr, "Couldn't allocate memory for frame buffer\n");
296  return 0;
297  }
298 
299  filter->prev = tmp;
300  filter->prev_size = size;
301 
302  return 1;
303 }
304 
305 static int imax(int a, int b) { return (a > b) ? a : b; }
306 
307 static int init_buf(ThisFilter *filter, VideoFrame *frame)
308 {
309  if (!alloc_prev(filter, frame->size))
310  return 0;
311 
312  int sz = imax(imax(frame->pitches[0], frame->pitches[1]), frame->pitches[2]);
313  if (!alloc_line(filter, sz))
314  return 0;
315 
316  if ((filter->prev_size != frame->size) ||
317  (filter->offsets[0] != frame->offsets[0]) ||
318  (filter->offsets[1] != frame->offsets[1]) ||
319  (filter->offsets[2] != frame->offsets[2]) ||
320  (filter->pitches[0] != frame->pitches[0]) ||
321  (filter->pitches[1] != frame->pitches[1]) ||
322  (filter->pitches[2] != frame->pitches[2]))
323  {
324  memcpy(filter->prev, frame->buf, frame->size);
325  memcpy(filter->offsets, frame->offsets, sizeof(int) * 3);
326  memcpy(filter->pitches, frame->pitches, sizeof(int) * 3);
327  }
328 
329  return 1;
330 }
331 
332 static int denoise3DFilter(VideoFilter *f, VideoFrame *frame, int field)
333 {
334  (void)field;
335  ThisFilter *filter = (ThisFilter*) f;
336  TF_VARS;
337 
338  if (!init_buf(filter, frame))
339  return -1;
340 
341  TF_START;
342 
343 #ifdef MMX
344  if (filter->mm_flags & AV_CPU_FLAG_MMX)
345  emms();
346 #endif
347 
348  (filter->filtfunc)(frame->buf + frame->offsets[0],
349  filter->prev + frame->offsets[0],
350  filter->line, frame->pitches[0], frame->height,
351  filter->coefs[0] + 256,
352  filter->coefs[1] + 256);
353 
354  (filter->filtfunc)(frame->buf + frame->offsets[1],
355  filter->prev + frame->offsets[1],
356  filter->line, frame->pitches[1], frame->height >> 1,
357  filter->coefs[2] + 256,
358  filter->coefs[3] + 256);
359 
360  (filter->filtfunc)(frame->buf + frame->offsets[2],
361  filter->prev + frame->offsets[2],
362  filter->line, frame->pitches[2], frame->height >> 1,
363  filter->coefs[2] + 256,
364  filter->coefs[3] + 256);
365 #ifdef MMX
366  if (filter->mm_flags & AV_CPU_FLAG_MMX)
367  emms();
368 #endif
369 
370  TF_END(filter, "Denoise3D: ");
371  return 0;
372 }
373 
375 {
376  if (((ThisFilter*)filter)->prev)
377  free(((ThisFilter*)filter)->prev);
378 
379  if (((ThisFilter*)filter)->line)
380  free (((ThisFilter*)filter)->line);
381 }
382 
384  VideoFrameType outpixfmt,
385  const int *width, const int *height, const char *options,
386  int threads)
387 {
388  double LumSpac = PARAM1_DEFAULT;
389  double LumTmp = PARAM3_DEFAULT;
390  double ChromSpac = PARAM2_DEFAULT;
391  double ChromTmp = 0.0;
392  ThisFilter *filter;
393 
394  (void) width;
395  (void) height;
396  (void) threads;
397  if (inpixfmt != FMT_YV12 || outpixfmt != FMT_YV12)
398  {
399  fprintf(stderr, "Denoise3D: attempt to initialize "
400  "with unsupported format\n");
401  return NULL;
402  }
403 
404  filter = malloc(sizeof (ThisFilter));
405  if (filter == NULL)
406  {
407  fprintf (stderr, "Denoise3D: failed to allocate memory for filter\n");
408  return NULL;
409  }
410 
411  memset(filter, 0, sizeof(ThisFilter));
412 
413  filter->vf.filter = &denoise3DFilter;
414  filter->vf.cleanup = &Denoise3DFilterCleanup;
415  filter->filtfunc = &denoise;
416 
417 #ifdef MMX
418  filter->mm_flags = av_get_cpu_flags();
419  if (filter->mm_flags & AV_CPU_FLAG_MMX)
420  filter->filtfunc = &denoiseMMX;
421 #endif
422 
423  TF_INIT(filter);
424 
425  if (options)
426  {
427  double param1, param2, param3;
428  switch (sscanf(options, "%20lf:%20lf:%20lf",
429  &param1, &param2, &param3))
430  {
431  case 0:
432  default:
433  break;
434 
435  case 1:
436  LumSpac = param1;
437  LumTmp = PARAM3_DEFAULT * param1 / PARAM1_DEFAULT;
438  ChromSpac = PARAM2_DEFAULT * param1 / PARAM1_DEFAULT;
439  break;
440 
441  case 2:
442  LumSpac = param1;
443  LumTmp = PARAM3_DEFAULT * param1 / PARAM1_DEFAULT;
444  ChromSpac = param2;
445  break;
446 
447  case 3:
448  LumSpac = param1;
449  LumTmp = param3;
450  ChromSpac = param2;
451  break;
452  }
453  }
454 
455  ChromTmp = LumTmp * ChromSpac / LumSpac;
456 
457  calc_coefs(filter->coefs[0], LumSpac);
458  calc_coefs(filter->coefs[1], LumTmp);
459  calc_coefs(filter->coefs[2], ChromSpac);
460  calc_coefs(filter->coefs[3], ChromTmp);
461 
462  return (VideoFilter*) filter;
463 }
464 
465 static FmtConv FmtList[] =
466 {
467  { FMT_YV12, FMT_YV12 },
468  FMT_NULL
469 };
470 
472 {
473  {
475  .name= (char*)"denoise3d",
476  .descript= (char*)
477  "removes noise with a spatial and temporal low-pass filter",
478  .formats= FmtList,
479  .libname= NULL
480  },
481  FILT_NULL
482 };
int pitches[3]
Y, U, & V pitches.
Definition: mythframe.h:63
const FilterInfo filter_table[]
int(* filter)(struct VideoFilter_ *, VideoFrame *, int)
Definition: filter.h:37
#define ABS(A)
init_filter filter_init
Definition: filter.h:28
stderr
Definition: ttvdb.py:1426
static void denoise(uint8_t *Frame, uint8_t *FramePrev, uint8_t *Line, int W, int H, const uint8_t *Spatial, const uint8_t *Temporal)
#define PARAM2_DEFAULT
uint8_t * line
#define TF_VARS
Definition: filter.h:112
#define NULL
Definition: H264Parser.h:62
#define LowPass(Prev, Curr, Coef)
void(* cleanup)(struct VideoFilter_ *)
Definition: filter.h:38
enum FrameType_ VideoFrameType
static void denoiseMMX(uint8_t *Frame, uint8_t *FramePrev, uint8_t *Line, int W, int H, const uint8_t *Spatial, const uint8_t *Temporal)
static int alloc_prev(ThisFilter *filter, int size)
static guint32 * tmp
Definition: goom_core.c:35
unsigned char b
Definition: ParseText.cpp:329
uint8_t * prev
static VideoFilter * NewDenoise3DFilter(VideoFrameType inpixfmt, VideoFrameType outpixfmt, const int *width, const int *height, const char *options, int threads)
int offsets[3]
Y, U, & V offsets.
Definition: mythframe.h:64
static int alloc_line(ThisFilter *filter, int size)
VideoFilter vf
Definition: filter_adjust.c:36
static FmtConv FmtList[]
int height
Definition: mythframe.h:42
struct ThisFilter ThisFilter
def log(debug, txt)
Definition: utilities.py:5
#define FMT_NULL
Definition: filter.h:20
#define mmx_t
#define TF_END(filter, prefix)
Definition: filter.h:114
static const mmx_t mz
#define emms()
Definition: mm_arch.h:15
#define FILT_NULL
Definition: filter.h:47
static void calc_coefs(uint8_t *Ct, double Dist25)
uint8_t coefs[4][512]
static int imax(int a, int b)
static int init_buf(ThisFilter *filter, VideoFrame *frame)
void(* filtfunc)(uint8_t *, uint8_t *, uint8_t *, int, int, const uint8_t *, const uint8_t *)
#define PARAM1_DEFAULT
#define TF_INIT(filter)
Definition: filter.h:110
static void Denoise3DFilterCleanup(VideoFilter *filter)
static int denoise3DFilter(VideoFilter *f, VideoFrame *frame, int field)
#define PARAM3_DEFAULT
#define TF_START
Definition: filter.h:113
unsigned char * buf
Definition: mythframe.h:39