MythTV  master
filter_quickdnr.c
Go to the documentation of this file.
1 /*
2  * Quick DNR 0.8
3  * (C)opyright 2003, Debabrata Banerjee
4  * GNU GPL 2 or later
5  *
6  * Pass options as:
7  * quickdnr=quality (0-255 scale adjusted)
8  * quickdnr=Luma_threshold:Chroma_threshold (0-255) for single threshold
9  * quickdnr=Luma_threshold1:Luma_threshold2:Chroma_threshold1:Chroma_threshold2 for double
10  *
11  */
12 
13 #include <stdio.h>
14 
15 #include "config.h"
16 #if HAVE_STDINT_H
17 #include <stdint.h>
18 #endif
19 
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include "filter.h"
24 #include "mythframe.h"
25 #include "libavutil/mem.h"
26 #include "libavutil/cpu.h"
27 
28 #ifdef MMX
29 #include "ffmpeg-mmx.h"
30 #endif
31 
32 //Regular filter
33 #define LUMA_THRESHOLD_DEFAULT 15
34 #define CHROMA_THRESHOLD_DEFAULT 25
35 
36 //Double thresholded filter
37 #define LUMA_THRESHOLD1_DEFAULT 10
38 #define LUMA_THRESHOLD2_DEFAULT 1
39 #define CHROMA_THRESHOLD1_DEFAULT 20
40 #define CHROMA_THRESHOLD2_DEFAULT 2
41 
42 //#define QUICKDNR_DEBUG
43 
44 //static const char FILTER_NAME[] = "quickdnr";
45 
46 typedef struct ThisFilter
47 {
49 
54  uint8_t Luma_threshold1;
55  uint8_t Luma_threshold2;
58  uint8_t *average;
60  int offsets[3];
61  int pitches[3];
62 
63  TF_STRUCT;
64 
65 } ThisFilter;
66 
67 static int alloc_avg(ThisFilter *filter, int size)
68 {
69  if (filter->average_size >= size)
70  return 1;
71 
72  uint8_t *tmp = realloc(filter->average, size);
73  if (!tmp)
74  {
75  fprintf(stderr, "Couldn't allocate memory for DNR buffer\n");
76  return 0;
77  }
78 
79  filter->average = tmp;
80  filter->average_size = size;
81 
82  return 1;
83 }
84 
85 static int init_avg(ThisFilter *filter, VideoFrame *frame)
86 {
87  if (!alloc_avg(filter, frame->size))
88  return 0;
89 
90  if ((filter->offsets[0] != frame->offsets[0]) ||
91  (filter->offsets[1] != frame->offsets[1]) ||
92  (filter->offsets[2] != frame->offsets[2]) ||
93  (filter->pitches[0] != frame->pitches[0]) ||
94  (filter->pitches[1] != frame->pitches[1]) ||
95  (filter->pitches[2] != frame->pitches[2]))
96  {
97  memcpy(filter->average, frame->buf, frame->size);
98  memcpy(filter->offsets, frame->offsets, sizeof(int) * 3);
99  memcpy(filter->pitches, frame->pitches, sizeof(int) * 3);
100  }
101 
102  return 1;
103 }
104 
105 static void init_vars(ThisFilter *tf, VideoFrame *frame,
106  int *thr1, int *thr2, int *height,
107  uint8_t **avg, uint8_t **buf)
108 {
109  thr1[0] = tf->Luma_threshold1;
110  thr1[1] = tf->Chroma_threshold1;
111  thr1[2] = tf->Chroma_threshold1;
112 
113  thr2[0] = tf->Luma_threshold2;
114  thr2[1] = tf->Chroma_threshold2;
115  thr2[2] = tf->Chroma_threshold2;
116 
117  height[0] = frame->height;
118  height[1] = frame->height >> 1;
119  height[2] = frame->height >> 1;
120 
121  avg[0] = tf->average + frame->offsets[0];
122  avg[1] = tf->average + frame->offsets[1];
123  avg[2] = tf->average + frame->offsets[2];
124 
125  buf[0] = frame->buf + frame->offsets[0];
126  buf[1] = frame->buf + frame->offsets[1];
127  buf[2] = frame->buf + frame->offsets[2];
128 }
129 
130 static int quickdnr(VideoFilter *f, VideoFrame *frame, int field)
131 {
132  (void)field;
133  ThisFilter *tf = (ThisFilter *)f;
134  int thr1[3], thr2[3], height[3];
135  uint8_t *avg[3], *buf[3];
136  int i, y;
137 
138  TF_VARS;
139 
140  TF_START;
141 
142  if (!init_avg(tf, frame))
143  return 0;
144 
145  init_vars(tf, frame, thr1, thr2, height, avg, buf);
146 
147  for (i = 0; i < 3; i++)
148  {
149  int sz = height[i] * frame->pitches[i];
150  for (y = 0; y < sz; y++)
151  {
152  if (abs(avg[i][y] - buf[i][y]) < thr1[i])
153  buf[i][y] = avg[i][y] = (avg[i][y] + buf[i][y]) >> 1;
154  else
155  avg[i][y] = buf[i][y];
156  }
157  }
158 
159  TF_END(tf, "QuickDNR: ");
160 
161  return 0;
162 }
163 
164 static int quickdnr2(VideoFilter *f, VideoFrame *frame, int field)
165 {
166  (void)field;
167  ThisFilter *tf = (ThisFilter *)f;
168  int thr1[3], thr2[3], height[3];
169  uint8_t *avg[3], *buf[3];
170  int i, y;
171 
172  TF_VARS;
173 
174  TF_START;
175 
176  if (!init_avg(tf, frame))
177  return 0;
178 
179  init_vars(tf, frame, thr1, thr2, height, avg, buf);
180 
181  for (i = 0; i < 3; i++)
182  {
183  int sz = height[i] * frame->pitches[i];
184  for (y = 0; y < sz; y++)
185  {
186  int t = abs(avg[i][y] - buf[i][y]);
187  if (t < thr1[i])
188  {
189  if (t > thr2[i])
190  avg[i][y] = (avg[i][y] + buf[i][y]) >> 1;
191  buf[i][y] = avg[i][y];
192  }
193  else
194  {
195  avg[i][y] = buf[i][y];
196  }
197  }
198  }
199 
200  TF_END(tf, "QuickDNR2: ");
201 
202  return 0;
203 }
204 
205 #ifdef MMX
206 
207 static int quickdnrMMX(VideoFilter *f, VideoFrame *frame, int field)
208 {
209  (void)field;
210  ThisFilter *tf = (ThisFilter *)f;
211  const uint64_t sign_convert = 0x8080808080808080LL;
212  int thr1[3], thr2[3], height[3];
213  uint64_t *avg[3], *buf[3];
214  int i, y;
215 
216  TF_VARS;
217 
218  TF_START;
219 
220  if (!init_avg(tf, frame))
221  return 0;
222 
223  init_vars(tf, frame, thr1, thr2, height, (uint8_t**) avg, (uint8_t**) buf);
224 
225  /*
226  Removed all the prefetches. These don't do anything when
227  you are processing an array with sequential accesses because the
228  processor automatically does a prefetchT0 in these cases. The
229  instruction is meant to be used to specify a different prefetch
230  cache level, or to prefetch non-sequental data.
231 
232  These prefetches are not available on all MMX processors so if
233  we wanted to use them we would need to test for a prefetch
234  capable processor before using them. -- dtk
235  */
236 
237  __asm__ volatile("emms\n\t");
238 
239  __asm__ volatile("movq (%0), %%mm4" : : "r" (&sign_convert));
240 
241  for (i = 0; i < 3; i++)
242  {
243  int sz = (height[i] * frame->pitches[i]) >> 3;
244 
245  if (0 == i)
246  __asm__ volatile("movq (%0), %%mm5" : : "r" (&tf->Luma_threshold_mask1));
247  else
248  __asm__ volatile("movq (%0), %%mm5" : : "r" (&tf->Chroma_threshold_mask1));
249 
250  for (y = 0; y < sz; y++)
251  {
252  __asm__ volatile(
253  "movq (%0), %%mm0 \n\t" // avg[i]
254  "movq (%1), %%mm1 \n\t" // buf[i]
255  "movq %%mm0, %%mm2 \n\t"
256  "movq %%mm1, %%mm3 \n\t"
257  "movq %%mm1, %%mm7 \n\t"
258 
259  "pcmpgtb %%mm0, %%mm1 \n\t" // 1 if av greater
260  "psubb %%mm0, %%mm3 \n\t" // mm3=buf-av
261  "psubb %%mm7, %%mm0 \n\t" // mm0=av-buf
262  "pand %%mm1, %%mm3 \n\t" // select buf
263  "pandn %%mm0,%%mm1 \n\t" // select av
264  "por %%mm1, %%mm3 \n\t" // mm3=abs()
265 
266  "paddb %%mm4, %%mm3 \n\t" // hack! No proper unsigned mmx compares!
267  "pcmpgtb %%mm5, %%mm3 \n\t" // compare buf with mask
268 
269  "pavgb %%mm7, %%mm2 \n\t"
270  "pand %%mm3, %%mm7 \n\t"
271  "pandn %%mm2,%%mm3 \n\t"
272  "por %%mm7, %%mm3 \n\t"
273  "movq %%mm3, (%0) \n\t"
274  "movq %%mm3, (%1) \n\t"
275  : : "r" (avg[i]), "r" (buf[i])
276  );
277  buf[i]++;
278  avg[i]++;
279  }
280  }
281 
282  __asm__ volatile("emms\n\t");
283 
284  // filter the leftovers from the mmx rutine
285  for (i = 0; i < 3; i++)
286  {
287  uint8_t *avg8[3], *buf8[3];
288  int end, beg;
289 
290  init_vars(tf, frame, thr1, thr2, height, avg8, buf8);
291 
292  end = height[i] * frame->pitches[i];
293  beg = end & ~0x7;
294 
295  if (beg == end)
296  continue;
297 
298  for (y = beg; y < end; y++)
299  {
300  if (abs(avg8[i][y] - buf8[i][y]) < thr1[i])
301  buf8[i][y] = avg8[i][y] = (avg8[i][y] + buf8[i][y]) >> 1;
302  else
303  avg8[i][y] = buf8[i][y];
304  }
305  }
306 
307  TF_END(tf, "QuickDNRmmx: ");
308 
309  return 0;
310 }
311 
312 
313 static int quickdnr2MMX(VideoFilter *f, VideoFrame *frame, int field)
314 {
315  (void)field;
316  ThisFilter *tf = (ThisFilter *)f;
317  const uint64_t sign_convert = 0x8080808080808080LL;
318  int thr1[3], thr2[3], height[3];
319  uint64_t *avg[3], *buf[3];
320  int i, y;
321 
322  TF_VARS;
323 
324  TF_START;
325 
326  if (!init_avg(tf, frame))
327  return 0;
328 
329  init_vars(tf, frame, thr1, thr2, height, (uint8_t**) avg, (uint8_t**) buf);
330 
331  __asm__ volatile("emms\n\t");
332 
333  __asm__ volatile("movq (%0), %%mm4" : : "r" (&sign_convert));
334 
335  for (i = 0; i < 3; i++)
336  {
337  int sz = (height[i] * frame->pitches[i]) >> 3;
338 
339  if (0 == i)
340  __asm__ volatile("movq (%0), %%mm5" : : "r" (&tf->Luma_threshold_mask1));
341  else
342  __asm__ volatile("movq (%0), %%mm5" : : "r" (&tf->Chroma_threshold_mask1));
343 
344  for (y = 0; y < sz; y++)
345  {
346  uint64_t *mask2 = (0 == i) ?
348 
349  __asm__ volatile(
350  "movq (%0), %%mm0 \n\t" // avg[i]
351  "movq (%1), %%mm1 \n\t" // buf[i]
352  "movq %%mm0, %%mm2 \n\t"
353  "movq %%mm1, %%mm3 \n\t"
354  "movq %%mm1, %%mm6 \n\t"
355  "movq %%mm1, %%mm7 \n\t"
356 
357  "pcmpgtb %%mm0, %%mm1 \n\t" // 1 if av greater
358  "psubb %%mm0, %%mm3 \n\t" // mm3=buf-av
359  "psubb %%mm7, %%mm0 \n\t" // mm0=av-buf
360  "pand %%mm1, %%mm3 \n\t" // select buf
361  "pandn %%mm0,%%mm1 \n\t" // select av
362  "por %%mm1, %%mm3 \n\t" // mm3=abs(buf-av)
363 
364  "paddb %%mm4, %%mm3 \n\t" // hack! No proper unsigned mmx compares!
365  "pcmpgtb %%mm5, %%mm3 \n\t" // compare diff with mask
366 
367  "movq %%mm2, %%mm0 \n\t" // reload registers
368  "movq %%mm7, %%mm1 \n\t"
369 
370  "pcmpgtb %%mm0, %%mm1 \n\t" // Secondary threshold
371  "psubb %%mm0, %%mm6 \n\t"
372  "psubb %%mm7, %%mm0 \n\t"
373  "pand %%mm1, %%mm6 \n\t"
374  "pandn %%mm0,%%mm1 \n\t"
375  "por %%mm1, %%mm6 \n\t"
376 
377  "paddb %%mm4, %%mm6 \n\t"
378  "pcmpgtb (%2), %%mm6 \n\t"
379 
380  "movq %%mm2, %%mm0 \n\t"
381 
382  "pavgb %%mm7, %%mm2 \n\t"
383 
384  "pand %%mm6, %%mm2 \n\t"
385  "pandn %%mm0,%%mm6 \n\t"
386  "por %%mm2, %%mm6 \n\t" // Combined new/keep average
387 
388  "pand %%mm3, %%mm7 \n\t"
389  "pandn %%mm6,%%mm3 \n\t"
390  "por %%mm7, %%mm3 \n\t" // Combined new/keep average
391 
392  "movq %%mm3, (%0) \n\t"
393  "movq %%mm3, (%1) \n\t"
394  : :
395  "r" (avg[i]),
396  "r" (buf[i]),
397  "r" (mask2)
398  );
399  buf[i]++;
400  avg[i]++;
401  }
402  }
403 
404  __asm__ volatile("emms\n\t");
405 
406  // filter the leftovers from the mmx rutine
407  for (i = 0; i < 3; i++)
408  {
409  int thr1a[3], thr2a[3], heighta[3];
410  uint8_t *avg8[3], *buf8[3];
411  int end, beg;
412 
413  init_vars(tf, frame, thr1a, thr2a, heighta, avg8, buf8);
414 
415  end = heighta[i] * frame->pitches[i];
416  beg = end & ~0x7;
417 
418  if (beg == end)
419  continue;
420 
421  for (y = beg; y < end; y++)
422  {
423  int t = abs(avg8[i][y] - buf8[i][y]);
424  if (t < thr1a[i])
425  {
426  if (t > thr2[i])
427  avg8[i][y] = (avg8[i][y] + buf8[i][y]) >> 1;
428  buf8[i][y] = avg8[i][y];
429  }
430  else
431  {
432  avg8[i][y] = buf8[i][y];
433  }
434  }
435  }
436 
437  TF_END(tf, "QuickDNR2mmx: ");
438 
439  return 0;
440 }
441 #endif /* MMX */
442 
443 static void cleanup(VideoFilter *vf)
444 {
445  ThisFilter *tf = (ThisFilter*) vf;
446 
447  if (tf->average)
448  free(tf->average);
449 }
450 
452  VideoFrameType outpixfmt,
453  const int *width, const int *height, const char *options,
454  int threads)
455 {
456  unsigned int Param1, Param2, Param3, Param4;
457  int i, double_threshold = 1;
458  ThisFilter *filter;
459 
460  (void) width;
461  (void) height;
462  (void) i;
463  (void) threads;
464 
465  if (inpixfmt != FMT_YV12 || outpixfmt != FMT_YV12)
466  {
467  fprintf(stderr, "QuickDNR: attempt to initialize "
468  "with unsupported format\n");
469  return NULL;
470  }
471 
472  filter = malloc(sizeof(ThisFilter));
473  if (filter == NULL)
474  {
475  fprintf(stderr, "Couldn't allocate memory for filter\n");
476  return NULL;
477  }
478 
479  memset(filter, 0, sizeof(ThisFilter));
480  filter->vf.cleanup = &cleanup;
485  double_threshold = 1;
486 
487  if (options)
488  {
489  int ret = sscanf(options, "%20u:%20u:%20u:%20u",
490  &Param1, &Param2, &Param3, &Param4);
491  switch (ret)
492  {
493  case 1:
494  //These might be better as logarithmic if this gets used a lot.
495  filter->Luma_threshold1 = ((uint8_t) Param1) * 40 / 255;
496  filter->Luma_threshold2 = ((uint8_t) Param1) * 4/255 > 2 ?
497  2 : ((uint8_t) Param1) * 4/255;
498  filter->Chroma_threshold1 = ((uint8_t) Param1) * 80 / 255;
499  filter->Chroma_threshold2 = ((uint8_t) Param1) * 8/255 > 4 ?
500  4 : ((uint8_t) Param1) * 8/255;
501  break;
502 
503  case 2:
504  filter->Luma_threshold1 = (uint8_t) Param1;
505  filter->Chroma_threshold1 = (uint8_t) Param2;
506  double_threshold = 0;
507  break;
508 
509  case 4:
510  filter->Luma_threshold1 = (uint8_t) Param1;
511  filter->Luma_threshold2 = (uint8_t) Param2;
512  filter->Chroma_threshold1 = (uint8_t) Param3;
513  filter->Chroma_threshold2 = (uint8_t) Param4;
514  break;
515 
516  default:
517  break;
518  }
519  }
520 
521  filter->vf.filter = (double_threshold) ? &quickdnr2 : &quickdnr;
522 
523 #ifdef MMX
524  if (av_get_cpu_flags() > AV_CPU_FLAG_MMX2)
525  {
526  filter->vf.filter = (double_threshold) ? &quickdnr2MMX : &quickdnrMMX;
527  for (i = 0; i < 8; i++)
528  {
529  // 8 sign-shifted bytes!
530  filter->Luma_threshold_mask1 =
531  (filter->Luma_threshold_mask1 << 8) +
532  ((filter->Luma_threshold1 > 0x80) ?
533  (filter->Luma_threshold1 - 0x80) :
534  (filter->Luma_threshold1 + 0x80));
535 
536  filter->Chroma_threshold_mask1 =
537  (filter->Chroma_threshold_mask1 << 8) +
538  ((filter->Chroma_threshold1 > 0x80) ?
539  (filter->Chroma_threshold1 - 0x80) :
540  (filter->Chroma_threshold1 + 0x80));
541 
542  filter->Luma_threshold_mask2 =
543  (filter->Luma_threshold_mask2 << 8) +
544  ((filter->Luma_threshold2 > 0x80) ?
545  (filter->Luma_threshold2 - 0x80) :
546  (filter->Luma_threshold2 + 0x80));
547 
548  filter->Chroma_threshold_mask2 =
549  (filter->Chroma_threshold_mask2 << 8) +
550  ((filter->Chroma_threshold2 > 0x80) ?
551  (filter->Chroma_threshold2 - 0x80) :
552  (filter->Chroma_threshold2 + 0x80));
553  }
554  }
555 #endif
556 
557  TF_INIT(filter);
558 
559 #ifdef QUICKDNR_DEBUG
560  fprintf(stderr, "DNR Loaded: 0x%X Params: %u %u \n"
561  "Luma1: %3d 0x%X%X Luma2: 0x%X%X\n"
562  "Chroma1: %3d %X%X Chroma2: 0x%X%X\n",
563  av_get_cpu_flags(), Param1, Param2, filter->Luma_threshold1,
564  ((int*)&filter->Luma_threshold_mask1)[1],
565  ((int*)&filter->Luma_threshold_mask1)[0],
566  ((int*)&filter->Luma_threshold_mask2)[1],
567  ((int*)&filter->Luma_threshold_mask2)[0],
568  filter->Chroma_threshold1,
569  ((int*)&filter->Chroma_threshold_mask1)[1],
570  ((int*)&filter->Chroma_threshold_mask1)[0],
571  ((int*)&filter->Chroma_threshold_mask2)[1],
572  ((int*)&filter->Chroma_threshold_mask2)[0]
573  );
574 
575  fprintf(stderr, "Options:%d:%d:%d:%d\n",
576  filter->Luma_threshold1, filter->Luma_threshold2,
577  filter->Chroma_threshold1, filter->Chroma_threshold2);
578 #endif
579 
580  return (VideoFilter*) filter;
581 }
582 
583 static FmtConv FmtList[] =
584 {
585  { FMT_YV12, FMT_YV12 },
586  FMT_NULL
587 };
588 
590 {
591  {
593  .name= (char*)"quickdnr",
594  .descript= (char*)
595  "removes noise with a fast single/double thresholded average filter",
596  .formats= FmtList,
597  .libname= NULL
598  },
599  FILT_NULL
600 };
int pitches[3]
Y, U, & V pitches.
Definition: mythframe.h:63
int(* filter)(struct VideoFilter_ *, VideoFrame *, int)
Definition: filter.h:37
uint8_t * average
init_filter filter_init
Definition: filter.h:28
stderr
Definition: ttvdb.py:1426
#define TF_VARS
Definition: filter.h:112
#define NULL
Definition: H264Parser.h:62
static VideoFilter * new_filter(VideoFrameType inpixfmt, VideoFrameType outpixfmt, const int *width, const int *height, const char *options, int threads)
void(* cleanup)(struct VideoFilter_ *)
Definition: filter.h:38
uint8_t Luma_threshold1
uint8_t Luma_threshold2
#define CHROMA_THRESHOLD2_DEFAULT
enum FrameType_ VideoFrameType
#define LUMA_THRESHOLD1_DEFAULT
static guint32 * tmp
Definition: goom_core.c:35
static int alloc_avg(ThisFilter *filter, int size)
static int quickdnr2MMX(VideoFilter *f, VideoFrame *frame, int field)
static int init_avg(ThisFilter *filter, VideoFrame *frame)
static int quickdnr2(VideoFilter *f, VideoFrame *frame, int field)
static int quickdnrMMX(VideoFilter *f, VideoFrame *frame, int field)
uint64_t Luma_threshold_mask2
int offsets[3]
Y, U, & V offsets.
Definition: mythframe.h:64
VideoFilter vf
Definition: filter_adjust.c:36
uint8_t Chroma_threshold1
int height
Definition: mythframe.h:42
unsigned char t
Definition: ParseText.cpp:329
uint64_t Chroma_threshold_mask1
uint64_t Chroma_threshold_mask2
static void init_vars(ThisFilter *tf, VideoFrame *frame, int *thr1, int *thr2, int *height, uint8_t **avg, uint8_t **buf)
static void cleanup(VideoFilter *vf)
static int quickdnr(VideoFilter *f, VideoFrame *frame, int field)
#define LUMA_THRESHOLD2_DEFAULT
static FmtConv FmtList[]
#define CHROMA_THRESHOLD1_DEFAULT
#define FMT_NULL
Definition: filter.h:20
#define TF_END(filter, prefix)
Definition: filter.h:114
struct ThisFilter ThisFilter
const FilterInfo filter_table[]
#define FILT_NULL
Definition: filter.h:47
uint8_t Chroma_threshold2
uint64_t Luma_threshold_mask1
#define TF_INIT(filter)
Definition: filter.h:110
#define TF_START
Definition: filter.h:113
unsigned char * buf
Definition: mythframe.h:39