| @@ -47,6 +47,12 @@ typedef struct ConvolveContext { · 46 unmodified lines |
| 47 | int planewidth[4]; | 47 | int planewidth[4]; |
| 48 | int planeheight[4]; | 48 | int planeheight[4]; |
| 49 | | 49 | |
| | 50 | int primarywidth[4]; |
| | 51 | int primaryheight[4]; |
| | 52 | |
| | 53 | int secondarywidth[4]; |
| | 54 | int secondaryheight[4]; |
| | 55 | |
| 50 | AVComplexFloat *fft_hdata_in[4]; | 56 | AVComplexFloat *fft_hdata_in[4]; |
| 51 | AVComplexFloat *fft_vdata_in[4]; | 57 | AVComplexFloat *fft_vdata_in[4]; |
| 52 | AVComplexFloat *fft_hdata_out[4]; | 58 | AVComplexFloat *fft_hdata_out[4]; |
| @@ -63,6 +69,13 @@ typedef struct ConvolveContext { · 10 unmodified lines |
| 63 | int nb_planes; | 69 | int nb_planes; |
| 64 | int got_impulse[4]; | 70 | int got_impulse[4]; |
| 65 | | 71 | |
| | 72 | void (*get_input)(struct ConvolveContext *s, AVComplexFloat *fft_hdata, |
| | 73 | AVFrame *in, int w, int h, int n, int plane, float scale); |
| | 74 | |
| | 75 | void (*get_output)(struct ConvolveContext *s, AVComplexFloat *input, AVFrame *out, |
| | 76 | int w, int h, int n, int plane, float scale); |
| | 77 | void (*prepare_impulse)(AVFilterContext *ctx, AVFrame *impulsepic, int plane); |
| | 78 | |
| 66 | int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); | 79 | int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); |
| 67 | } ConvolveContext; | 80 | } ConvolveContext; |
| 68 | | 81 | |
| @@ -99,21 +112,22 @@ static const enum AVPixelFormat pixel_fmts_fftfilt[] = { · 30 unmodified lines |
| 99 | AV_PIX_FMT_NONE | 112 | AV_PIX_FMT_NONE |
| 100 | }; | 113 | }; |
| 101 | | 114 | |
| 102 | static int config_input_main(AVFilterLink *inlink) | 115 | static int config_input(AVFilterLink *inlink) |
| 103 | { | 116 | { |
| 104 | ConvolveContext *s = inlink->dst->priv; | 117 | ConvolveContext *s = inlink->dst->priv; |
| 105 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); | 118 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
| 106 | int i; | 119 | const int w = inlink->w; |
| | 120 | const int h = inlink->h; |
| 107 | | 121 | |
| 108 | s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); | 122 | s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(w, desc->log2_chroma_w); |
| 109 | s->planewidth[0] = s->planewidth[3] = inlink->w; | 123 | s->planewidth[0] = s->planewidth[3] = w; |
| 110 | s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); | 124 | s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(h, desc->log2_chroma_h); |
| 111 | s->planeheight[0] = s->planeheight[3] = inlink->h; | 125 | s->planeheight[0] = s->planeheight[3] = h; |
| 112 | | 126 | |
| 113 | s->nb_planes = desc->nb_components; | 127 | s->nb_planes = desc->nb_components; |
| 114 | s->depth = desc->comp[0].depth; | 128 | s->depth = desc->comp[0].depth; |
| 115 | | 129 | |
| 116 | for (i = 0; i < s->nb_planes; i++) { | 130 | for (int i = 0; i < s->nb_planes; i++) { |
| 117 | int w = s->planewidth[i]; | 131 | int w = s->planewidth[i]; |
| 118 | int h = s->planeheight[i]; | 132 | int h = s->planeheight[i]; |
| 119 | int n = FFMAX(w, h); | 133 | int n = FFMAX(w, h); |
| @@ -186,6 +200,98 @@ static int fft_horizontal(AVFilterContext *ctx, void *arg, int jobnr, int nb_job · 66 unmodified lines |
| 186 | return 0; | 200 | return 0; |
| 187 | } | 201 | } |
| 188 | | 202 | |
| | 203 | #define SQR(x) ((x) * (x)) |
| | 204 | |
| | 205 | static void get_zeropadded_input(ConvolveContext *s, |
| | 206 | AVComplexFloat *fft_hdata, |
| | 207 | AVFrame *in, int w, int h, |
| | 208 | int n, int plane, float scale) |
| | 209 | { |
| | 210 | float sum = 0.f; |
| | 211 | float mean, dev; |
| | 212 | int y, x; |
| | 213 | |
| | 214 | if (s->depth == 8) { |
| | 215 | for (y = 0; y < h; y++) { |
| | 216 | const uint8_t *src = in->data[plane] + in->linesize[plane] * y; |
| | 217 | |
| | 218 | for (x = 0; x < w; x++) |
| | 219 | sum += src[x]; |
| | 220 | } |
| | 221 | |
| | 222 | mean = sum / (w * h); |
| | 223 | sum = 0.f; |
| | 224 | for (y = 0; y < h; y++) { |
| | 225 | const uint8_t *src = in->data[plane] + in->linesize[plane] * y; |
| | 226 | |
| | 227 | for (x = 0; x < w; x++) |
| | 228 | sum += SQR(src[x] - mean); |
| | 229 | } |
| | 230 | |
| | 231 | dev = sqrtf(sum / (w * h)); |
| | 232 | scale /= dev; |
| | 233 | for (y = 0; y < h; y++) { |
| | 234 | const uint8_t *src = in->data[plane] + in->linesize[plane] * y; |
| | 235 | |
| | 236 | for (x = 0; x < w; x++) { |
| | 237 | fft_hdata[y * n + x].re = (src[x] - mean) * scale; |
| | 238 | fft_hdata[y * n + x].im = 0; |
| | 239 | } |
| | 240 | |
| | 241 | for (x = w; x < n; x++) { |
| | 242 | fft_hdata[y * n + x].re = 0; |
| | 243 | fft_hdata[y * n + x].im = 0; |
| | 244 | } |
| | 245 | } |
| | 246 | |
| | 247 | for (y = h; y < n; y++) { |
| | 248 | for (x = 0; x < n; x++) { |
| | 249 | fft_hdata[y * n + x].re = 0; |
| | 250 | fft_hdata[y * n + x].im = 0; |
| | 251 | } |
| | 252 | } |
| | 253 | } else { |
| | 254 | for (y = 0; y < h; y++) { |
| | 255 | const uint16_t *src = (const uint16_t *)(in->data[plane] + in->linesize[plane] * y); |
| | 256 | |
| | 257 | for (x = 0; x < w; x++) |
| | 258 | sum += src[x]; |
| | 259 | } |
| | 260 | |
| | 261 | mean = sum / (w * h); |
| | 262 | sum = 0.f; |
| | 263 | for (y = 0; y < h; y++) { |
| | 264 | const uint16_t *src = (const uint16_t *)(in->data[plane] + in->linesize[plane] * y); |
| | 265 | |
| | 266 | for (x = 0; x < w; x++) |
| | 267 | sum += SQR(src[x] - mean); |
| | 268 | } |
| | 269 | |
| | 270 | dev = sqrtf(sum / (w * h)); |
| | 271 | scale /= dev; |
| | 272 | for (y = 0; y < h; y++) { |
| | 273 | const uint16_t *src = (const uint16_t *)(in->data[plane] + in->linesize[plane] * y); |
| | 274 | |
| | 275 | for (x = 0; x < w; x++) { |
| | 276 | fft_hdata[y * n + x].re = (src[x] - mean) * scale; |
| | 277 | fft_hdata[y * n + x].im = 0; |
| | 278 | } |
| | 279 | |
| | 280 | for (x = w; x < n; x++) { |
| | 281 | fft_hdata[y * n + x].re = 0; |
| | 282 | fft_hdata[y * n + x].im = 0; |
| | 283 | } |
| | 284 | } |
| | 285 | |
| | 286 | for (y = h; y < n; y++) { |
| | 287 | for (x = 0; x < n; x++) { |
| | 288 | fft_hdata[y * n + x].re = 0; |
| | 289 | fft_hdata[y * n + x].im = 0; |
| | 290 | } |
| | 291 | } |
| | 292 | } |
| | 293 | } |
| | 294 | |
| 189 | static void get_input(ConvolveContext *s, AVComplexFloat *fft_hdata, | 295 | static void get_input(ConvolveContext *s, AVComplexFloat *fft_hdata, |
| 190 | AVFrame *in, int w, int h, int n, int plane, float scale) | 296 | AVFrame *in, int w, int h, int n, int plane, float scale) |
| 191 | { | 297 | { |
| @@ -330,6 +436,27 @@ static int ifft_horizontal(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo · 138 unmodified lines |
| 330 | return 0; | 436 | return 0; |
| 331 | } | 437 | } |
| 332 | | 438 | |
| | 439 | static void get_xoutput(ConvolveContext *s, AVComplexFloat *input, AVFrame *out, |
| | 440 | int w, int h, int n, int plane, float scale) |
| | 441 | { |
| | 442 | const int imax = (1 << s->depth) - 1; |
| | 443 | |
| | 444 | scale *= imax * 16; |
| | 445 | if (s->depth == 8) { |
| | 446 | for (int y = 0; y < h; y++) { |
| | 447 | uint8_t *dst = out->data[plane] + y * out->linesize[plane]; |
| | 448 | for (int x = 0; x < w; x++) |
| | 449 | dst[x] = av_clip_uint8(input[y * n + x].re * scale); |
| | 450 | } |
| | 451 | } else { |
| | 452 | for (int y = 0; y < h; y++) { |
| | 453 | uint16_t *dst = (uint16_t *)(out->data[plane] + y * out->linesize[plane]); |
| | 454 | for (int x = 0; x < w; x++) |
| | 455 | dst[x] = av_clip(input[y * n + x].re * scale, 0, imax); |
| | 456 | } |
| | 457 | } |
| | 458 | } |
| | 459 | |
| 333 | static void get_output(ConvolveContext *s, AVComplexFloat *input, AVFrame *out, | 460 | static void get_output(ConvolveContext *s, AVComplexFloat *input, AVFrame *out, |
| 334 | int w, int h, int n, int plane, float scale) | 461 | int w, int h, int n, int plane, float scale) |
| 335 | { | 462 | { |
| @@ -414,6 +541,35 @@ static int complex_multiply(AVFilterContext *ctx, void *arg, int jobnr, int nb_j · 78 unmodified lines |
| 414 | return 0; | 541 | return 0; |
| 415 | } | 542 | } |
| 416 | | 543 | |
| | 544 | static int complex_xcorrelate(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) |
| | 545 | { |
| | 546 | ThreadData *td = arg; |
| | 547 | AVComplexFloat *input = td->hdata_in; |
| | 548 | AVComplexFloat *filter = td->vdata_in; |
| | 549 | const int n = td->n; |
| | 550 | const float scale = 1.f / (n * n); |
| | 551 | int start = (n * jobnr) / nb_jobs; |
| | 552 | int end = (n * (jobnr+1)) / nb_jobs; |
| | 553 | |
| | 554 | for (int y = start; y < end; y++) { |
| | 555 | int yn = y * n; |
| | 556 | |
| | 557 | for (int x = 0; x < n; x++) { |
| | 558 | float re, im, ire, iim; |
| | 559 | |
| | 560 | re = input[yn + x].re; |
| | 561 | im = input[yn + x].im; |
| | 562 | ire = filter[yn + x].re * scale; |
| | 563 | iim = -filter[yn + x].im * scale; |
| | 564 | |
| | 565 | input[yn + x].re = ire * re - iim * im; |
| | 566 | input[yn + x].im = iim * re + ire * im; |
| | 567 | } |
| | 568 | } |
| | 569 | |
| | 570 | return 0; |
| | 571 | } |
| | 572 | |
| 417 | static int complex_divide(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | 573 | static int complex_divide(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) |
| 418 | { | 574 | { |
| 419 | ConvolveContext *s = ctx->priv; | 575 | ConvolveContext *s = ctx->priv; |
| @@ -446,13 +602,82 @@ static int complex_divide(AVFilterContext *ctx, void *arg, int jobnr, int nb_job · 26 unmodified lines |
| 446 | return 0; | 602 | return 0; |
| 447 | } | 603 | } |
| 448 | | 604 | |
| | 605 | static void prepare_impulse(AVFilterContext *ctx, AVFrame *impulsepic, int plane) |
| | 606 | { |
| | 607 | ConvolveContext *s = ctx->priv; |
| | 608 | const int n = s->fft_len[plane]; |
| | 609 | const int w = s->secondarywidth[plane]; |
| | 610 | const int h = s->secondaryheight[plane]; |
| | 611 | ThreadData td; |
| | 612 | float total = 0; |
| | 613 | |
| | 614 | if (s->depth == 8) { |
| | 615 | for (int y = 0; y < h; y++) { |
| | 616 | const uint8_t *src = (const uint8_t *)(impulsepic->data[plane] + y * impulsepic->linesize[plane]) ; |
| | 617 | for (int x = 0; x < w; x++) { |
| | 618 | total += src[x]; |
| | 619 | } |
| | 620 | } |
| | 621 | } else { |
| | 622 | for (int y = 0; y < h; y++) { |
| | 623 | const uint16_t *src = (const uint16_t *)(impulsepic->data[plane] + y * impulsepic->linesize[plane]) ; |
| | 624 | for (int x = 0; x < w; x++) { |
| | 625 | total += src[x]; |
| | 626 | } |
| | 627 | } |
| | 628 | } |
| | 629 | total = FFMAX(1, total); |
| | 630 | |
| | 631 | s->get_input(s, s->fft_hdata_impulse_in[plane], impulsepic, w, h, n, plane, 1.f / total); |
| | 632 | |
| | 633 | td.n = n; |
| | 634 | td.plane = plane; |
| | 635 | td.hdata_in = s->fft_hdata_impulse_in[plane]; |
| | 636 | td.vdata_in = s->fft_vdata_impulse_in[plane]; |
| | 637 | td.hdata_out = s->fft_hdata_impulse_out[plane]; |
| | 638 | td.vdata_out = s->fft_vdata_impulse_out[plane]; |
| | 639 | |
| | 640 | ff_filter_execute(ctx, fft_horizontal, &td, NULL, |
| | 641 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); |
| | 642 | ff_filter_execute(ctx, fft_vertical, &td, NULL, |
| | 643 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); |
| | 644 | |
| | 645 | s->got_impulse[plane] = 1; |
| | 646 | } |
| | 647 | |
| | 648 | static void prepare_secondary(AVFilterContext *ctx, AVFrame *secondary, int plane) |
| | 649 | { |
| | 650 | ConvolveContext *s = ctx->priv; |
| | 651 | const int n = s->fft_len[plane]; |
| | 652 | ThreadData td; |
| | 653 | |
| | 654 | s->get_input(s, s->fft_hdata_impulse_in[plane], secondary, |
| | 655 | s->secondarywidth[plane], |
| | 656 | s->secondaryheight[plane], |
| | 657 | n, plane, 1.f); |
| | 658 | |
| | 659 | td.n = n; |
| | 660 | td.plane = plane; |
| | 661 | td.hdata_in = s->fft_hdata_impulse_in[plane]; |
| | 662 | td.vdata_in = s->fft_vdata_impulse_in[plane]; |
| | 663 | td.hdata_out = s->fft_hdata_impulse_out[plane]; |
| | 664 | td.vdata_out = s->fft_vdata_impulse_out[plane]; |
| | 665 | |
| | 666 | ff_filter_execute(ctx, fft_horizontal, &td, NULL, |
| | 667 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); |
| | 668 | ff_filter_execute(ctx, fft_vertical, &td, NULL, |
| | 669 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); |
| | 670 | |
| | 671 | s->got_impulse[plane] = 1; |
| | 672 | } |
| | 673 | |
| 449 | static int do_convolve(FFFrameSync *fs) | 674 | static int do_convolve(FFFrameSync *fs) |
| 450 | { | 675 | { |
| 451 | AVFilterContext *ctx = fs->parent; | 676 | AVFilterContext *ctx = fs->parent; |
| 452 | AVFilterLink *outlink = ctx->outputs[0]; | 677 | AVFilterLink *outlink = ctx->outputs[0]; |
| 453 | ConvolveContext *s = ctx->priv; | 678 | ConvolveContext *s = ctx->priv; |
| 454 | AVFrame *mainpic = NULL, *impulsepic = NULL; | 679 | AVFrame *mainpic = NULL, *impulsepic = NULL; |
| 455 | int ret, y, x, plane; | 680 | int ret, plane; |
| 456 | | 681 | |
| 457 | ret = ff_framesync_dualinput_get(fs, &mainpic, &impulsepic); | 682 | ret = ff_framesync_dualinput_get(fs, &mainpic, &impulsepic); |
| 458 | if (ret < 0) | 683 | if (ret < 0) |
| @@ -464,9 +689,10 @@ static int do_convolve(FFFrameSync *fs) · 5 unmodified lines |
| 464 | AVComplexFloat *filter = s->fft_vdata_impulse_out[plane]; | 689 | AVComplexFloat *filter = s->fft_vdata_impulse_out[plane]; |
| 465 | AVComplexFloat *input = s->fft_vdata_out[plane]; | 690 | AVComplexFloat *input = s->fft_vdata_out[plane]; |
| 466 | const int n = s->fft_len[plane]; | 691 | const int n = s->fft_len[plane]; |
| 467 | const int w = s->planewidth[plane]; | 692 | const int w = s->primarywidth[plane]; |
| 468 | const int h = s->planeheight[plane]; | 693 | const int h = s->primaryheight[plane]; |
| 469 | float total = 0; | 694 | const int ow = s->planewidth[plane]; |
| | 695 | const int oh = s->planeheight[plane]; |
| 470 | ThreadData td; | 696 | ThreadData td; |
| 471 | | 697 | |
| 472 | if (!(s->planes & (1 << plane))) { | 698 | if (!(s->planes & (1 << plane))) { |
| @@ -474,7 +700,7 @@ static int do_convolve(FFFrameSync *fs) · 1 unmodified line |
| 474 | } | 700 | } |
| 475 | | 701 | |
| 476 | td.plane = plane, td.n = n; | 702 | td.plane = plane, td.n = n; |
| 477 | get_input(s, s->fft_hdata_in[plane], mainpic, w, h, n, plane, 1.f); | 703 | s->get_input(s, s->fft_hdata_in[plane], mainpic, w, h, n, plane, 1.f); |
| 478 | | 704 | |
| 479 | td.hdata_in = s->fft_hdata_in[plane]; | 705 | td.hdata_in = s->fft_hdata_in[plane]; |
| 480 | td.vdata_in = s->fft_vdata_in[plane]; | 706 | td.vdata_in = s->fft_vdata_in[plane]; |
| @@ -487,36 +713,7 @@ static int do_convolve(FFFrameSync *fs) · 6 unmodified lines |
| 487 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); | 713 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); |
| 488 | | 714 | |
| 489 | if ((!s->impulse && !s->got_impulse[plane]) || s->impulse) { | 715 | if ((!s->impulse && !s->got_impulse[plane]) || s->impulse) { |
| 490 | if (s->depth == 8) { | 716 | s->prepare_impulse(ctx, impulsepic, plane); |
| 491 | for (y = 0; y < h; y++) { | | |
| 492 | const uint8_t *src = (const uint8_t *)(impulsepic->data[plane] + y * impulsepic->linesize[plane]) ; | | |
| 493 | for (x = 0; x < w; x++) { | | |
| 494 | total += src[x]; | | |
| 495 | } | | |
| 496 | } | | |
| 497 | } else { | | |
| 498 | for (y = 0; y < h; y++) { | | |
| 499 | const uint16_t *src = (const uint16_t *)(impulsepic->data[plane] + y * impulsepic->linesize[plane]) ; | | |
| 500 | for (x = 0; x < w; x++) { | | |
| 501 | total += src[x]; | | |
| 502 | } | | |
| 503 | } | | |
| 504 | } | | |
| 505 | total = FFMAX(1, total); | | |
| 506 | | | |
| 507 | get_input(s, s->fft_hdata_impulse_in[plane], impulsepic, w, h, n, plane, 1.f / total); | | |
| 508 | | | |
| 509 | td.hdata_in = s->fft_hdata_impulse_in[plane]; | | |
| 510 | td.vdata_in = s->fft_vdata_impulse_in[plane]; | | |
| 511 | td.hdata_out = s->fft_hdata_impulse_out[plane]; | | |
| 512 | td.vdata_out = s->fft_vdata_impulse_out[plane]; | | |
| 513 | | | |
| 514 | ff_filter_execute(ctx, fft_horizontal, &td, NULL, | | |
| 515 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); | | |
| 516 | ff_filter_execute(ctx, fft_vertical, &td, NULL, | | |
| 517 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); | | |
| 518 | | | |
| 519 | s->got_impulse[plane] = 1; | | |
| 520 | } | 717 | } |
| 521 | | 718 | |
| 522 | td.hdata_in = input; | 719 | td.hdata_in = input; |
| @@ -539,7 +736,7 @@ static int do_convolve(FFFrameSync *fs) · 16 unmodified lines |
| 539 | ff_filter_execute(ctx, ifft_horizontal, &td, NULL, | 736 | ff_filter_execute(ctx, ifft_horizontal, &td, NULL, |
| 540 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); | 737 | FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx))); |
| 541 | | 738 | |
| 542 | get_output(s, s->fft_hdata_out[plane], mainpic, w, h, n, plane, 1.f / (n * n)); | 739 | s->get_output(s, s->fft_hdata_out[plane], mainpic, ow, oh, n, plane, 1.f / (n * n)); |
| 543 | } | 740 | } |
| 544 | | 741 | |
| 545 | return ff_filter_frame(outlink, mainpic); | 742 | return ff_filter_frame(outlink, mainpic); |
| @@ -547,11 +744,23 @@ static int do_convolve(FFFrameSync *fs) · 1 unmodified line |
| 547 | | 744 | |
| 548 | static int config_output(AVFilterLink *outlink) | 745 | static int config_output(AVFilterLink *outlink) |
| 549 | { | 746 | { |
| | 747 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); |
| 550 | AVFilterContext *ctx = outlink->src; | 748 | AVFilterContext *ctx = outlink->src; |
| 551 | ConvolveContext *s = ctx->priv; | 749 | ConvolveContext *s = ctx->priv; |
| 552 | AVFilterLink *mainlink = ctx->inputs[0]; | 750 | AVFilterLink *mainlink = ctx->inputs[0]; |
| | 751 | AVFilterLink *secondlink = ctx->inputs[1]; |
| 553 | int ret, i, j; | 752 | int ret, i, j; |
| 554 | | 753 | |
| | 754 | s->primarywidth[1] = s->primarywidth[2] = AV_CEIL_RSHIFT(mainlink->w, desc->log2_chroma_w); |
| | 755 | s->primarywidth[0] = s->primarywidth[3] = mainlink->w; |
| | 756 | s->primaryheight[1] = s->primaryheight[2] = AV_CEIL_RSHIFT(mainlink->h, desc->log2_chroma_h); |
| | 757 | s->primaryheight[0] = s->primaryheight[3] = mainlink->h; |
| | 758 | |
| | 759 | s->secondarywidth[1] = s->secondarywidth[2] = AV_CEIL_RSHIFT(secondlink->w, desc->log2_chroma_w); |
| | 760 | s->secondarywidth[0] = s->secondarywidth[3] = secondlink->w; |
| | 761 | s->secondaryheight[1] = s->secondaryheight[2] = AV_CEIL_RSHIFT(secondlink->h, desc->log2_chroma_h); |
| | 762 | s->secondaryheight[0] = s->secondaryheight[3] = secondlink->h; |
| | 763 | |
| 555 | s->fs.on_event = do_convolve; | 764 | s->fs.on_event = do_convolve; |
| 556 | ret = ff_framesync_init_dualinput(&s->fs, ctx); | 765 | ret = ff_framesync_init_dualinput(&s->fs, ctx); |
| 557 | if (ret < 0) | 766 | if (ret < 0) |
| @@ -593,8 +802,19 @@ static av_cold int init(AVFilterContext *ctx) · 35 unmodified lines |
| 593 | | 802 | |
| 594 | if (!strcmp(ctx->filter->name, "convolve")) { | 803 | if (!strcmp(ctx->filter->name, "convolve")) { |
| 595 | s->filter = complex_multiply; | 804 | s->filter = complex_multiply; |
| | 805 | s->prepare_impulse = prepare_impulse; |
| | 806 | s->get_input = get_input; |
| | 807 | s->get_output = get_output; |
| | 808 | } else if (!strcmp(ctx->filter->name, "xcorrelate")) { |
| | 809 | s->filter = complex_xcorrelate; |
| | 810 | s->prepare_impulse = prepare_secondary; |
| | 811 | s->get_input = get_zeropadded_input; |
| | 812 | s->get_output = get_xoutput; |
| 596 | } else if (!strcmp(ctx->filter->name, "deconvolve")) { | 813 | } else if (!strcmp(ctx->filter->name, "deconvolve")) { |
| 597 | s->filter = complex_divide; | 814 | s->filter = complex_divide; |
| | 815 | s->prepare_impulse = prepare_impulse; |
| | 816 | s->get_input = get_input; |
| | 817 | s->get_output = get_output; |
| 598 | } else { | 818 | } else { |
| 599 | return AVERROR_BUG; | 819 | return AVERROR_BUG; |
| 600 | } | 820 | } |
| @@ -630,7 +850,7 @@ static const AVFilterPad convolve_inputs[] = { · 29 unmodified lines |
| 630 | { | 850 | { |
| 631 | .name = "main", | 851 | .name = "main", |
| 632 | .type = AVMEDIA_TYPE_VIDEO, | 852 | .type = AVMEDIA_TYPE_VIDEO, |
| 633 | .config_props = config_input_main, | 853 | .config_props = config_input, |
| 634 | },{ | 854 | },{ |
| 635 | .name = "impulse", | 855 | .name = "impulse", |
| 636 | .type = AVMEDIA_TYPE_VIDEO, | 856 | .type = AVMEDIA_TYPE_VIDEO, |
| @@ -698,3 +918,65 @@ const AVFilter ff_vf_deconvolve = { · 61 unmodified lines |
| 698 | }; | 918 | }; |
| 699 | | 919 | |
| 700 | #endif /* CONFIG_DECONVOLVE_FILTER */ | 920 | #endif /* CONFIG_DECONVOLVE_FILTER */ |
| | 921 | |
| | 922 | #if CONFIG_XCORRELATE_FILTER |
| | 923 | |
| | 924 | static const AVOption xcorrelate_options[] = { |
| | 925 | { "planes", "set planes to cross-correlate", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, |
| | 926 | { "secondary", "when to process secondary frame", OFFSET(impulse), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "impulse" }, |
| | 927 | { "first", "process only first secondary frame, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "impulse" }, |
| | 928 | { "all", "process all secondary frames", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "impulse" }, |
| | 929 | { NULL }, |
| | 930 | }; |
| | 931 | |
| | 932 | FRAMESYNC_DEFINE_PURE_CLASS(xcorrelate, "xcorrelate", convolve, xcorrelate_options); |
| | 933 | |
| | 934 | static int config_input_secondary(AVFilterLink *inlink) |
| | 935 | { |
| | 936 | AVFilterContext *ctx = inlink->dst; |
| | 937 | |
| | 938 | if (ctx->inputs[0]->w <= ctx->inputs[1]->w || |
| | 939 | ctx->inputs[0]->h <= ctx->inputs[1]->h) { |
| | 940 | av_log(ctx, AV_LOG_ERROR, "Width and height of second input videos must be less than first input.\n"); |
| | 941 | return AVERROR(EINVAL); |
| | 942 | } |
| | 943 | |
| | 944 | return 0; |
| | 945 | } |
| | 946 | |
| | 947 | static const AVFilterPad xcorrelate_inputs[] = { |
| | 948 | { |
| | 949 | .name = "primary", |
| | 950 | .type = AVMEDIA_TYPE_VIDEO, |
| | 951 | .config_props = config_input, |
| | 952 | },{ |
| | 953 | .name = "secondary", |
| | 954 | .type = AVMEDIA_TYPE_VIDEO, |
| | 955 | .config_props = config_input_secondary, |
| | 956 | }, |
| | 957 | }; |
| | 958 | |
| | 959 | static const AVFilterPad xcorrelate_outputs[] = { |
| | 960 | { |
| | 961 | .name = "default", |
| | 962 | .type = AVMEDIA_TYPE_VIDEO, |
| | 963 | .config_props = config_output, |
| | 964 | }, |
| | 965 | }; |
| | 966 | |
| | 967 | const AVFilter ff_vf_xcorrelate = { |
| | 968 | .name = "xcorrelate", |
| | 969 | .description = NULL_IF_CONFIG_SMALL("Cross-correlate first video stream with second video stream."), |
| | 970 | .preinit = convolve_framesync_preinit, |
| | 971 | .init = init, |
| | 972 | .uninit = uninit, |
| | 973 | .activate = activate, |
| | 974 | .priv_size = sizeof(ConvolveContext), |
| | 975 | .priv_class = &xcorrelate_class, |
| | 976 | FILTER_INPUTS(xcorrelate_inputs), |
| | 977 | FILTER_OUTPUTS(xcorrelate_outputs), |
| | 978 | FILTER_PIXFMTS_ARRAY(pixel_fmts_fftfilt), |
| | 979 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, |
| | 980 | }; |
| | 981 | |
| | 982 | #endif /* CONFIG_XCORRELATE_FILTER */ |