00001
00002
00003
00004
00005
00006
00007 #include <stdlib.h>
00008 #include <stdio.h>
00009
00010 #include "mythconfig.h"
00011 #if HAVE_STDINT_H
00012 #include <stdint.h>
00013 #endif
00014
00015 #include <string.h>
00016 #include <math.h>
00017
00018 #include "filter.h"
00019 #include "frame.h"
00020 #include "../mm_arch.h"
00021
00022 #define PARAM1_DEFAULT 4.0
00023 #define PARAM2_DEFAULT 3.0
00024 #define PARAM3_DEFAULT 6.0
00025
00026 #define LowPass(Prev, Curr, Coef) (Curr + Coef[Prev - Curr])
00027
00028 #undef ABS
00029 #define ABS(A) ( (A) > 0 ? (A) : -(A) )
00030
00031 #ifdef MMX
00032 #include "ffmpeg-mmx.h"
00033 static const mmx_t mz = { 0x0LL };
00034 #endif
00035
00036 typedef struct ThisFilter
00037 {
00038 VideoFilter vf;
00039
00040 int offsets[3];
00041 int pitches[3];
00042 int mm_flags;
00043 int line_size;
00044 int prev_size;
00045 uint8_t *line;
00046 uint8_t *prev;
00047 uint8_t coefs[4][512];
00048
00049 void (*filtfunc)(uint8_t*, uint8_t*, uint8_t*,
00050 int, int, uint8_t*, uint8_t*);
00051
00052 TF_STRUCT;
00053 } ThisFilter;
00054
00055 static void calc_coefs(uint8_t * Ct, double Dist25)
00056 {
00057 int i;
00058 double Gamma, Simil, C;
00059
00060 Gamma = log (0.25) / log (1.0 - Dist25 / 255.0);
00061
00062 for (i = -256; i <= 255; i++)
00063 {
00064 Simil = 1.0 - ABS (i) / 255.0;
00065 C = pow (Simil, Gamma) * (double) i;
00066 Ct[256 + i] = (C < 0) ? (C - 0.5) : (C + 0.5);
00067 }
00068 }
00069
00070 static void denoise(uint8_t *Frame,
00071 uint8_t *FramePrev,
00072 uint8_t *Line,
00073 int W, int H,
00074 uint8_t *Spatial, uint8_t *Temporal)
00075 {
00076 uint8_t prev;
00077 int X, Y;
00078 uint8_t *LineCur = Frame;
00079 uint8_t *LinePrev = FramePrev;
00080
00081 prev = Line[0] = Frame[0];
00082 Frame[0] = LowPass (FramePrev[0], Frame[1], Temporal);
00083 for (X = 1; X < W; X++)
00084 {
00085 prev = LowPass (prev, Frame[X], Spatial);
00086 Line[X] = prev;
00087 FramePrev[X] = Frame[X] = LowPass (FramePrev[X], prev, Temporal);
00088 }
00089
00090 for (Y = 1; Y < H; Y++)
00091 {
00092 LineCur += W;
00093 LinePrev += W;
00094 prev = LineCur[0];
00095 Line[0] = LowPass (Line[0], prev, Spatial);
00096 LineCur[0] = LowPass (LinePrev[0], Line[0], Temporal);
00097 for (X = 1; X < W; X++)
00098 {
00099 prev = LowPass (prev, LineCur[X], Spatial);
00100 Line[X] = LowPass (Line[X], prev, Spatial);
00101 LinePrev[X] = LineCur[X] = LowPass (LinePrev[X], Line[X], Temporal);
00102 }
00103 }
00104 }
00105
00106 #ifdef MMX
00107
00108 static void denoiseMMX(uint8_t *Frame,
00109 uint8_t *FramePrev,
00110 uint8_t *Line,
00111 int W, int H,
00112 uint8_t *Spatial, uint8_t *Temporal)
00113 {
00114 int X, i;
00115 uint8_t *LineCur = Frame;
00116 uint8_t *LinePrev = FramePrev;
00117 uint8_t *End = Frame + W * H;
00118 int16_t wbuf[16];
00119 uint8_t cbuf[16];
00120
00121 Line[0] = LineCur[0];
00122 for (X = 1; X < W; X++)
00123 Line[X] = LowPass (Line[X-1], LineCur[X], Spatial);
00124
00125 for (X = 0; X < W - 15; X += 16)
00126 {
00127 movq_m2r (LinePrev[X], mm0);
00128 movq_m2r (LinePrev[X+8], mm2);
00129 movq_m2r (Line[X], mm4);
00130 movq_m2r (Line[X+8], mm6);
00131 movq_r2r (mm0, mm1);
00132 movq_r2r (mm2, mm3);
00133 movq_r2r (mm4, mm5);
00134 movq_r2r (mm6, mm7);
00135
00136 punpcklbw_m2r(mz, mm0);
00137 punpckhbw_m2r(mz, mm1);
00138 punpcklbw_m2r(mz, mm2);
00139 punpckhbw_m2r(mz, mm3);
00140 punpcklbw_m2r(mz, mm4);
00141 punpckhbw_m2r(mz, mm5);
00142 punpcklbw_m2r(mz, mm6);
00143 punpckhbw_m2r(mz, mm7);
00144
00145 psubw_r2r (mm4, mm0);
00146 psubw_r2r (mm5, mm1);
00147 psubw_r2r (mm6, mm2);
00148 psubw_r2r (mm7, mm3);
00149
00150 movq_r2m (mm0, wbuf[0]);
00151 movq_r2m (mm1, wbuf[4]);
00152 movq_r2m (mm2, wbuf[8]);
00153 movq_r2m (mm3, wbuf[12]);
00154
00155 movq_m2r (Line[X], mm4);
00156 movq_m2r (Line[X+8], mm6);
00157
00158 for (i = 0; i < 16; i++)
00159 cbuf[i] = Temporal[wbuf[i]];
00160
00161 paddb_m2r (cbuf[0], mm4);
00162 paddb_m2r (cbuf[8], mm6);
00163 movq_r2m (mm4, LinePrev[X]);
00164 movq_r2m (mm6, LinePrev[X+8]);
00165 movq_r2m (mm4, LineCur[X]);
00166 movq_r2m (mm6, LineCur[X+8]);
00167 }
00168
00169 for (; X < W; X++)
00170 LineCur[X] = Line[X] = LowPass (LinePrev[X], Line[X], Temporal);
00171
00172 LineCur += W;
00173 LinePrev += W;
00174 while (LineCur < End)
00175 {
00176 for (X = 1; X < W; X++)
00177 LineCur[X] = LowPass (LineCur[X-1], LineCur[X], Spatial);
00178
00179 for (X = 0; X < W - 15; X += 16)
00180 {
00181 movq_m2r (Line[X], mm0);
00182 movq_m2r (Line[X+8], mm2);
00183 movq_m2r (LineCur[X], mm4);
00184 movq_m2r (LineCur[X+8], mm6);
00185 movq_r2r (mm0, mm1);
00186 movq_r2r (mm2, mm3);
00187 movq_r2r (mm4, mm5);
00188 movq_r2r (mm6, mm7);
00189
00190 punpcklbw_m2r(mz, mm0);
00191 punpckhbw_m2r(mz, mm1);
00192 punpcklbw_m2r(mz, mm2);
00193 punpckhbw_m2r(mz, mm3);
00194 punpcklbw_m2r(mz, mm4);
00195 punpckhbw_m2r(mz, mm5);
00196 punpcklbw_m2r(mz, mm6);
00197 punpckhbw_m2r(mz, mm7);
00198
00199 psubw_r2r (mm4, mm0);
00200 psubw_r2r (mm5, mm1);
00201 psubw_r2r (mm6, mm2);
00202 psubw_r2r (mm7, mm3);
00203
00204 movq_r2m (mm0, wbuf[0]);
00205 movq_r2m (mm1, wbuf[4]);
00206 movq_r2m (mm2, wbuf[8]);
00207 movq_r2m (mm3, wbuf[12]);
00208
00209 movq_m2r (LineCur[X], mm4);
00210 movq_m2r (LineCur[X+8], mm6);
00211
00212 for (i = 0; i < 16; i++)
00213 cbuf[i] = Spatial[wbuf[i]];
00214
00215 movq_m2r (LinePrev[X], mm0);
00216 movq_m2r (LinePrev[X+8], mm2);
00217 paddb_m2r (cbuf[0], mm4);
00218 paddb_m2r (cbuf[8], mm6);
00219 movq_r2m (mm4, Line[X]);
00220 movq_r2m (mm6, Line[X+8]);
00221
00222 movq_r2r (mm0, mm1);
00223 movq_r2r (mm2, mm3);
00224 movq_r2r (mm4, mm5);
00225 movq_r2r (mm6, mm7);
00226
00227 punpcklbw_m2r(mz, mm0);
00228 punpckhbw_m2r(mz, mm1);
00229 punpcklbw_m2r(mz, mm2);
00230 punpckhbw_m2r(mz, mm3);
00231 punpcklbw_m2r(mz, mm4);
00232 punpckhbw_m2r(mz, mm5);
00233 punpcklbw_m2r(mz, mm6);
00234 punpckhbw_m2r(mz, mm7);
00235
00236 psubw_r2r (mm4, mm0);
00237 psubw_r2r (mm5, mm1);
00238 psubw_r2r (mm6, mm2);
00239 psubw_r2r (mm7, mm3);
00240
00241 movq_r2m (mm0, wbuf[0]);
00242 movq_r2m (mm1, wbuf[4]);
00243 movq_r2m (mm2, wbuf[8]);
00244 movq_r2m (mm3, wbuf[12]);
00245
00246 movq_m2r (Line[X], mm4);
00247 movq_m2r (Line[X+8], mm6);
00248
00249 for (i = 0; i < 16; i++)
00250 cbuf[i] = Temporal[wbuf[i]];
00251
00252 paddb_m2r (cbuf[0], mm4);
00253 paddb_m2r (cbuf[8], mm6);
00254 movq_r2m (mm4, LinePrev[X]);
00255 movq_r2m (mm6, LinePrev[X+8]);
00256 movq_r2m (mm4, LineCur[X]);
00257 movq_r2m (mm6, LineCur[X+8]);
00258 }
00259
00260 for (; X < W; X++)
00261 {
00262 Line[X] = LowPass (Line[X], LineCur[X], Spatial);
00263 LineCur[X] = LinePrev[X] = LowPass (LinePrev[X], Line[X], Temporal);
00264 }
00265
00266 LineCur += W;
00267 LinePrev += W;
00268 }
00269 }
00270 #endif
00271
00272 static int alloc_line(ThisFilter *filter, int size)
00273 {
00274 if (filter->line_size >= size)
00275 return 1;
00276
00277 uint8_t *tmp = realloc(filter->line, size);
00278 if (!tmp)
00279 {
00280 fprintf(stderr, "Couldn't allocate memory for line buffer\n");
00281 return 0;
00282 }
00283
00284 filter->line = tmp;
00285 filter->line_size = size;
00286
00287 return 1;
00288 }
00289
00290 static int alloc_prev(ThisFilter *filter, int size)
00291 {
00292 if (filter->prev_size >= size)
00293 return 1;
00294
00295 uint8_t *tmp = realloc(filter->prev, size);
00296 if (!tmp)
00297 {
00298 fprintf(stderr, "Couldn't allocate memory for frame buffer\n");
00299 return 0;
00300 }
00301
00302 filter->prev = tmp;
00303 filter->prev_size = size;
00304
00305 return 1;
00306 }
00307
00308 static int imax(int a, int b) { return (a > b) ? a : b; }
00309
00310 static int init_buf(ThisFilter *filter, VideoFrame *frame)
00311 {
00312 if (!alloc_prev(filter, frame->size))
00313 return 0;
00314
00315 int sz = imax(imax(frame->pitches[0], frame->pitches[1]), frame->pitches[2]);
00316 if (!alloc_line(filter, sz))
00317 return 0;
00318
00319 if ((filter->prev_size != frame->size) ||
00320 (filter->offsets[0] != frame->offsets[0]) ||
00321 (filter->offsets[1] != frame->offsets[1]) ||
00322 (filter->offsets[2] != frame->offsets[2]) ||
00323 (filter->pitches[0] != frame->pitches[0]) ||
00324 (filter->pitches[1] != frame->pitches[1]) ||
00325 (filter->pitches[2] != frame->pitches[2]))
00326 {
00327 memcpy(filter->prev, frame->buf, frame->size);
00328 memcpy(filter->offsets, frame->offsets, sizeof(int) * 3);
00329 memcpy(filter->pitches, frame->pitches, sizeof(int) * 3);
00330 }
00331
00332 return 1;
00333 }
00334
00335 static int denoise3DFilter(VideoFilter *f, VideoFrame *frame, int field)
00336 {
00337 (void)field;
00338 ThisFilter *filter = (ThisFilter*) f;
00339 TF_VARS;
00340
00341 if (!init_buf(filter, frame))
00342 return -1;
00343
00344 TF_START;
00345
00346 #ifdef MMX
00347 if (filter->mm_flags & AV_CPU_FLAG_MMX)
00348 emms();
00349 #endif
00350
00351 (filter->filtfunc)(frame->buf + frame->offsets[0],
00352 filter->prev + frame->offsets[0],
00353 filter->line, frame->pitches[0], frame->height,
00354 filter->coefs[0] + 256,
00355 filter->coefs[1] + 256);
00356
00357 (filter->filtfunc)(frame->buf + frame->offsets[1],
00358 filter->prev + frame->offsets[1],
00359 filter->line, frame->pitches[1], frame->height >> 1,
00360 filter->coefs[2] + 256,
00361 filter->coefs[3] + 256);
00362
00363 (filter->filtfunc)(frame->buf + frame->offsets[2],
00364 filter->prev + frame->offsets[2],
00365 filter->line, frame->pitches[2], frame->height >> 1,
00366 filter->coefs[2] + 256,
00367 filter->coefs[3] + 256);
00368 #ifdef MMX
00369 if (filter->mm_flags & AV_CPU_FLAG_MMX)
00370 emms();
00371 #endif
00372
00373 TF_END(filter, "Denoise3D: ");
00374 return 0;
00375 }
00376
00377 static void Denoise3DFilterCleanup(VideoFilter *filter)
00378 {
00379 if (((ThisFilter*)filter)->prev)
00380 free(((ThisFilter*)filter)->prev);
00381
00382 if (((ThisFilter*)filter)->line)
00383 free (((ThisFilter*)filter)->line);
00384 }
00385
00386 static VideoFilter *NewDenoise3DFilter(VideoFrameType inpixfmt,
00387 VideoFrameType outpixfmt,
00388 int *width, int *height, char *options,
00389 int threads)
00390 {
00391 double LumSpac = PARAM1_DEFAULT;
00392 double LumTmp = PARAM3_DEFAULT;
00393 double ChromSpac = PARAM2_DEFAULT;
00394 double ChromTmp = 0.0;
00395 ThisFilter *filter;
00396
00397 (void) width;
00398 (void) height;
00399 (void) threads;
00400 if (inpixfmt != FMT_YV12 || outpixfmt != FMT_YV12)
00401 {
00402 fprintf(stderr, "Denoise3D: attempt to initialize "
00403 "with unsupported format\n");
00404 return NULL;
00405 }
00406
00407 filter = malloc(sizeof (ThisFilter));
00408 if (filter == NULL)
00409 {
00410 fprintf (stderr, "Denoise3D: failed to allocate memory for filter\n");
00411 return NULL;
00412 }
00413
00414 memset(filter, 0, sizeof(ThisFilter));
00415
00416 filter->vf.filter = &denoise3DFilter;
00417 filter->vf.cleanup = &Denoise3DFilterCleanup;
00418 filter->filtfunc = &denoise;
00419
00420 #ifdef MMX
00421 filter->mm_flags = av_get_cpu_flags();
00422 if (filter->mm_flags & AV_CPU_FLAG_MMX)
00423 filter->filtfunc = &denoiseMMX;
00424 #endif
00425
00426 TF_INIT(filter);
00427
00428 if (options)
00429 {
00430 double param1, param2, param3;
00431 switch (sscanf(options, "%20lf:%20lf:%20lf",
00432 ¶m1, ¶m2, ¶m3))
00433 {
00434 case 0:
00435 default:
00436 break;
00437
00438 case 1:
00439 LumSpac = param1;
00440 LumTmp = PARAM3_DEFAULT * param1 / PARAM1_DEFAULT;
00441 ChromSpac = PARAM2_DEFAULT * param1 / PARAM1_DEFAULT;
00442 break;
00443
00444 case 2:
00445 LumSpac = param1;
00446 LumTmp = PARAM3_DEFAULT * param1 / PARAM1_DEFAULT;
00447 ChromSpac = param2;
00448 break;
00449
00450 case 3:
00451 LumSpac = param1;
00452 LumTmp = param3;
00453 ChromSpac = param2;
00454 break;
00455 }
00456 }
00457
00458 ChromTmp = LumTmp * ChromSpac / LumSpac;
00459
00460 calc_coefs(filter->coefs[0], LumSpac);
00461 calc_coefs(filter->coefs[1], LumTmp);
00462 calc_coefs(filter->coefs[2], ChromSpac);
00463 calc_coefs(filter->coefs[3], ChromTmp);
00464
00465 return (VideoFilter*) filter;
00466 }
00467
00468 static FmtConv FmtList[] =
00469 {
00470 { FMT_YV12, FMT_YV12 },
00471 FMT_NULL
00472 };
00473
00474 ConstFilterInfo filter_table[] =
00475 {
00476 {
00477 filter_init: &NewDenoise3DFilter,
00478 name: "denoise3d",
00479 descript: "removes noise with a spatial and temporal low-pass filter",
00480 formats: FmtList,
00481 libname: NULL
00482 },
00483 FILT_NULL
00484 };