diff mbox series

[FFmpeg-devel] avfilter: add radial and circular blur video filter

Message ID 20200718161203.16643-1-onemda@gmail.com
State New
Headers show
Series [FFmpeg-devel] avfilter: add radial and circular blur video filter | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Paul B Mahol July 18, 2020, 4:12 p.m. UTC
Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 doc/filters.texi         |  46 ++++
 libavfilter/Makefile     |   2 +
 libavfilter/allfilters.c |   2 +
 libavfilter/vf_rblur.c   | 560 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 610 insertions(+)
 create mode 100644 libavfilter/vf_rblur.c

Comments

Jean-Baptiste Kempf July 19, 2020, 3:35 p.m. UTC | #1
On Sun, 19 Jul 2020, at 16:05, Paul B Mahol wrote:
> You all dislike me on discrimination basis and also object just for objecting.

Discriminate you based on what?
Noone has ever seen you, and therefore noone knows your gender, religion, skin color, race, or any other personal thing on you.
Nicolas George July 19, 2020, 3:39 p.m. UTC | #2
Paul B Mahol (12020-07-19):
> I do not need to listen to you at all.
> You all dislike me on discrimination basis and also object just for objecting.
> 
> When I have objections to any patches they are silently ignored, other
> way around
> is different as this patch yet again demonstrates.

I will not deny that the fact that I feel you have repeatedly abused me
and used similar tactics against me makes pay extra attention to cases
where I think you may abuse other contributors, and intervene if I think
it necessary. If I am at fault here, we now have a committee that can
put me right, and I welcome its judgement on my case.

But it does not excuse you for threatening to ignore Steinar's concerns.
Steinar H. Gunderson July 19, 2020, 4:02 p.m. UTC | #3
On Sun, Jul 19, 2020 at 03:58:32PM +0200, Nicolas George wrote:
>> Will apply.
> Certainly not before Steinar has had time to review it and tell us
> whether you addressed all the concerns.

The primary change that I see is the addition of bilinear filtering in the
polar to Cartesian step. This makes the filter even slower, but it also
increases the output quality significantly. There are still artifacts I've
never seen in any other implementation of radial blur, and that stem directly
from the choice of algorithm. The comment situation is unchanged, ie., there
are none.

If someone else thinks the patch as-is is acceptable, I won't block it,
in the interest of less hostility.

/* Steinar */
Paul B Mahol July 19, 2020, 4:13 p.m. UTC | #4
On 7/19/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
> On Sun, Jul 19, 2020 at 03:58:32PM +0200, Nicolas George wrote:
>>> Will apply.
>> Certainly not before Steinar has had time to review it and tell us
>> whether you addressed all the concerns.
>
> The primary change that I see is the addition of bilinear filtering in the
> polar to Cartesian step. This makes the filter even slower, but it also
> increases the output quality significantly. There are still artifacts I've
> never seen in any other implementation of radial blur, and that stem
> directly
> from the choice of algorithm. The comment situation is unchanged, ie., there
> are none.
>
> If someone else thinks the patch as-is is acceptable, I won't block it,
> in the interest of less hostility.


Thanks for confirming you are being hostile.

>
> /* Steinar */
> --
> Homepage: https://www.sesse.net/
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol July 19, 2020, 4:14 p.m. UTC | #5
On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
> On Sun, 19 Jul 2020, at 16:05, Paul B Mahol wrote:
>> You all dislike me on discrimination basis and also object just for
>> objecting.
>
> Discriminate you based on what?
> Noone has ever seen you, and therefore noone knows your gender, religion,
> skin color, race, or any other personal thing on you.

You are now lying.
Mr. President you are indeed lying.

>
> --
> Jean-Baptiste Kempf -  President
> +33 672 704 734
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Jean-Baptiste Kempf July 19, 2020, 4:17 p.m. UTC | #6
On Sun, 19 Jul 2020, at 18:14, Paul B Mahol wrote:
> On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
> > On Sun, 19 Jul 2020, at 16:05, Paul B Mahol wrote:
> >> You all dislike me on discrimination basis and also object just for
> >> objecting.
> >
> > Discriminate you based on what?
> > Noone has ever seen you, and therefore noone knows your gender, religion,
> > skin color, race, or any other personal thing on you.
> 
> You are now lying.

How so?
Paul B Mahol July 19, 2020, 4:17 p.m. UTC | #7
On 7/19/20, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12020-07-19):
>> I do not need to listen to you at all.
>> You all dislike me on discrimination basis and also object just for
>> objecting.
>>
>> When I have objections to any patches they are silently ignored, other
>> way around
>> is different as this patch yet again demonstrates.
>
> I will not deny that the fact that I feel you have repeatedly abused me
> and used similar tactics against me makes pay extra attention to cases
> where I think you may abuse other contributors, and intervene if I think
> it necessary. If I am at fault here, we now have a committee that can
> put me right, and I welcome its judgement on my case.
>
> But it does not excuse you for threatening to ignore Steinar's concerns.

Who abused you? I even asked why one of your patches you posted are not applied.
I even vouched for untile filter to be applied.

But than you came so high and polite and threatens me for applying
against your blockage of my numerous patches out of pure hatred.

>
> --
>   Nicolas George
>
Paul B Mahol July 19, 2020, 4:20 p.m. UTC | #8
On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
> On Sun, 19 Jul 2020, at 18:14, Paul B Mahol wrote:
>> On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
>> > On Sun, 19 Jul 2020, at 16:05, Paul B Mahol wrote:
>> >> You all dislike me on discrimination basis and also object just for
>> >> objecting.
>> >
>> > Discriminate you based on what?
>> > Noone has ever seen you, and therefore noone knows your gender,
>> > religion,
>> > skin color, race, or any other personal thing on you.
>>
>> You are now lying.
>
> How so?

You discriminate me just because I send big patches.
While you, in your deepest self could never do such thing.

>
> --
> Jean-Baptiste Kempf -  President
> +33 672 704 734
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Jean-Baptiste Kempf July 19, 2020, 4:25 p.m. UTC | #9
On Sun, 19 Jul 2020, at 18:20, Paul B Mahol wrote:
> On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
> > On Sun, 19 Jul 2020, at 18:14, Paul B Mahol wrote:
> >> On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
> >> > On Sun, 19 Jul 2020, at 16:05, Paul B Mahol wrote:
> >> >> You all dislike me on discrimination basis and also object just for
> >> >> objecting.
> >> >
> >> > Discriminate you based on what?
> >> > Noone has ever seen you, and therefore noone knows your gender,
> >> > religion,
> >> > skin color, race, or any other personal thing on you.
> >>
> >> You are now lying.
> >
> > How so?
> 
> You discriminate me just because I send big patches.

"Discrimination is the act of making distinctions between human beings based on the groups, classes, or other categories to which they are perceived to belong. "

I really doubt "sending big patches" fit the discrimination definition.

And even if it did, I really doubt that the push-back from "all of us"  you are seeing is because of discrimination, but more about your attitude towards others.

Here, Steinar gives a substantiated remark on your code, which could improve quality and speed, which matches the goal of this project.

Why not just apply what he did, and give us a better filter? 
This is not discrimination, just noone understand your points.
Paul B Mahol July 19, 2020, 4:37 p.m. UTC | #10
On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
> On Sun, 19 Jul 2020, at 18:20, Paul B Mahol wrote:
>> On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
>> > On Sun, 19 Jul 2020, at 18:14, Paul B Mahol wrote:
>> >> On 7/19/20, Jean-Baptiste Kempf <jb@videolan.org> wrote:
>> >> > On Sun, 19 Jul 2020, at 16:05, Paul B Mahol wrote:
>> >> >> You all dislike me on discrimination basis and also object just for
>> >> >> objecting.
>> >> >
>> >> > Discriminate you based on what?
>> >> > Noone has ever seen you, and therefore noone knows your gender,
>> >> > religion,
>> >> > skin color, race, or any other personal thing on you.
>> >>
>> >> You are now lying.
>> >
>> > How so?
>>
>> You discriminate me just because I send big patches.
>
> "Discrimination is the act of making distinctions between human beings based
> on the groups, classes, or other categories to which they are perceived to
> belong. "
>
> I really doubt "sending big patches" fit the discrimination definition.
>
> And even if it did, I really doubt that the push-back from "all of us"  you
> are seeing is because of discrimination, but more about your attitude
> towards others.
>
> Here, Steinar gives a substantiated remark on your code, which could improve
> quality and speed, which matches the goal of this project.
>
> Why not just apply what he did, and give us a better filter?
> This is not discrimination, just noone understand your points.

Dear Jean-Baptiste Kempf, if I knew how to implement what Steinar wish
I would do
it already. Clearly there is big miss-understanding here.

Steinar could implement such filter already if he knew how to do it.
I think he does not know that he does not know that algorithm.

The only thing I could propose which would make filter faster under
same parameters
is to cache all results of atan2f / sqrtf calls.

About radial blur center issues, i think Steinar is confusing radial
blur with zoom blur.
Jean-Baptiste Kempf July 19, 2020, 4:40 p.m. UTC | #11
On Sun, 19 Jul 2020, at 18:37, Paul B Mahol wrote:
> > Here, Steinar gives a substantiated remark on your code, which could improve
> > quality and speed, which matches the goal of this project.
> >
> > Why not just apply what he did, and give us a better filter?
> > This is not discrimination, just noone understand your points.
> 
> Dear Jean-Baptiste Kempf, if I knew how to implement what Steinar wish
> I would do
> it already. Clearly there is big miss-understanding here.

You see, it would be simpler just to say that, in answer to Steinar, instead of starting a flamewar, noone knows the reason of.

> Steinar could implement such filter already if he knew how to do it.
> I think he does not know that he does not know that algorithm.

I think Steinar knows a bit about filters algorithms:
https://git.sesse.net/?p=movit;a=tree , but as all of us, he is human, so he can also make mistakes.
James Almer July 19, 2020, 5:03 p.m. UTC | #12
On 7/19/2020 1:13 PM, Paul B Mahol wrote:
> On 7/19/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
>> On Sun, Jul 19, 2020 at 03:58:32PM +0200, Nicolas George wrote:
>>>> Will apply.
>>> Certainly not before Steinar has had time to review it and tell us
>>> whether you addressed all the concerns.
>>
>> The primary change that I see is the addition of bilinear filtering in the
>> polar to Cartesian step. This makes the filter even slower, but it also
>> increases the output quality significantly. There are still artifacts I've
>> never seen in any other implementation of radial blur, and that stem
>> directly
>> from the choice of algorithm. The comment situation is unchanged, ie., there
>> are none.
>>
>> If someone else thinks the patch as-is is acceptable, I won't block it,
>> in the interest of less hostility.
> 
> 
> Thanks for confirming you are being hostile.

He did no such thing.

Please Paul, if you can implement his suggestions then do so, and if you
can't then state as much and maybe explain why. But this entire
discussion thread is absurd and had no reason to happen.
Steinar H. Gunderson July 19, 2020, 5:28 p.m. UTC | #13
On Sun, Jul 19, 2020 at 06:37:16PM +0200, Paul B Mahol wrote:
> About radial blur center issues, i think Steinar is confusing radial
> blur with zoom blur.

I must admit I don't know any such difference; this is one of many things
that would be useful to have in comments or the commit message (many such
effects have different variations under similar names). But I do not
believe this is relevant to the artifacts in the center, or the staircase
effect on the horizontal white lines -- no matter what you call an filter,
they are not inherent to the effect. Both are much less pronounced than in
the previous patch set, but still present.

For those who want to judge for themselves:

  https://storage.sesse.net/ffmpeg-radialblur3.png

/* Steinar */
Paul B Mahol July 19, 2020, 5:32 p.m. UTC | #14
On 7/19/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
> On Sun, Jul 19, 2020 at 06:37:16PM +0200, Paul B Mahol wrote:
>> About radial blur center issues, i think Steinar is confusing radial
>> blur with zoom blur.
>
> I must admit I don't know any such difference; this is one of many things
> that would be useful to have in comments or the commit message (many such
> effects have different variations under similar names). But I do not
> believe this is relevant to the artifacts in the center, or the staircase
> effect on the horizontal white lines -- no matter what you call an filter,
> they are not inherent to the effect. Both are much less pronounced than in
> the previous patch set, but still present.
>
> For those who want to judge for themselves:
>
>   https://storage.sesse.net/ffmpeg-radialblur3.png
>

I see nothing wrong with that image.

You still provided 0 valuable information for your perfect solution algorithm.

> /* Steinar */
> --
> Homepage: https://www.sesse.net/
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Steinar H. Gunderson July 19, 2020, 6:06 p.m. UTC | #15
On Sun, Jul 19, 2020 at 07:32:18PM +0200, Paul B Mahol wrote:
> I see nothing wrong with that image.

Let me provide some zooms. First, center artifacts (the “flower” should not
be there):

  http://storage.sesse.net/ffmpeg-radialblur3-zoom1.png

Then, staircase/uneven horizontal line near the edge (the line should not
wobble up and down):

  http://storage.sesse.net/ffmpeg-radialblur3-zoom2.png

> You still provided 0 valuable information for your perfect solution algorithm.

I haven't claimed any perfect solutions, but I have suggested two different
algorithms with details that I both believe would be more performant and
sport higher output quality (actually three, but the last one was not with
a lot of details). Could you suggest which information you would regard as
valuable?

Here's the source code for GIMP's zoom blur implementation, which works
without any similar artifacts, and has no trigonometry per-pixel:

  https://gitlab.gnome.org/GNOME/gegl/-/blob/master/operations/common-gpl3+/motion-blur-zoom.c#L160

If you can explain what you mean the difference between radial and zoom blur
is, it would probably be possible to adapt the idea.

It also has a somewhat more efficient implementation of bilinear filtering
than in your patch (three multiplies instead of eight, and also fewer
add/sub), although it's likely that a fixed-point implementation would save
yet more time if you're interested in trying that.

/* Steinar */
Paul B Mahol July 19, 2020, 6:20 p.m. UTC | #16
On 7/19/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
> On Sun, Jul 19, 2020 at 07:32:18PM +0200, Paul B Mahol wrote:
>> I see nothing wrong with that image.
>
> Let me provide some zooms. First, center artifacts (the “flower” should not
> be there):
>
>   http://storage.sesse.net/ffmpeg-radialblur3-zoom1.png
>
> Then, staircase/uneven horizontal line near the edge (the line should not
> wobble up and down):
>
>   http://storage.sesse.net/ffmpeg-radialblur3-zoom2.png

Huh, since when we look at such small differences?

>
>> You still provided 0 valuable information for your perfect solution
>> algorithm.
>
> I haven't claimed any perfect solutions, but I have suggested two different
> algorithms with details that I both believe would be more performant and
> sport higher output quality (actually three, but the last one was not with
> a lot of details). Could you suggest which information you would regard as
> valuable?
>
> Here's the source code for GIMP's zoom blur implementation, which works
> without any similar artifacts, and has no trigonometry per-pixel:
>
>
> https://gitlab.gnome.org/GNOME/gegl/-/blob/master/operations/common-gpl3+/motion-blur-zoom.c#L160
>
> If you can explain what you mean the difference between radial and zoom blur
> is, it would probably be possible to adapt the idea.

That algorithm is very slow for big lengths, it takes ages to process
single small image.

>
> It also has a somewhat more efficient implementation of bilinear filtering
> than in your patch (three multiplies instead of eight, and also fewer
> add/sub), although it's likely that a fixed-point implementation would save
> yet more time if you're interested in trying that.

Such micro-optimizations could be added any time later.
Steinar H. Gunderson July 19, 2020, 6:28 p.m. UTC | #17
On Sun, Jul 19, 2020 at 08:20:40PM +0200, Paul B Mahol wrote:
>> If you can explain what you mean the difference between radial and zoom blur
>> is, it would probably be possible to adapt the idea.
> That algorithm is very slow for big lengths, it takes ages to process
> single small image.

Yes, this is the non-recursive version, which is O(n) in the number of
samples. This is why I recommended the recursive version, which is O(log n)
in the number of samples and thus avoids the problem with big lengths.
It is fairly straightforward to convert one to the other.

>> It also has a somewhat more efficient implementation of bilinear filtering
>> than in your patch (three multiplies instead of eight, and also fewer
>> add/sub), although it's likely that a fixed-point implementation would save
>> yet more time if you're interested in trying that.
> Such micro-optimizations could be added any time later.

Sure.

/* Steinar */
Paul B Mahol July 19, 2020, 7:02 p.m. UTC | #18
On 7/19/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
> On Sun, Jul 19, 2020 at 08:20:40PM +0200, Paul B Mahol wrote:
>>> If you can explain what you mean the difference between radial and zoom
>>> blur
>>> is, it would probably be possible to adapt the idea.
>> That algorithm is very slow for big lengths, it takes ages to process
>> single small image.
>
> Yes, this is the non-recursive version, which is O(n) in the number of
> samples. This is why I recommended the recursive version, which is O(log n)
> in the number of samples and thus avoids the problem with big lengths.
> It is fairly straightforward to convert one to the other.

Really? How so?

>
>>> It also has a somewhat more efficient implementation of bilinear
>>> filtering
>>> than in your patch (three multiplies instead of eight, and also fewer
>>> add/sub), although it's likely that a fixed-point implementation would
>>> save
>>> yet more time if you're interested in trying that.
>> Such micro-optimizations could be added any time later.
>
> Sure.
>
> /* Steinar */
> --
> Homepage: https://www.sesse.net/
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Steinar H. Gunderson July 19, 2020, 7:09 p.m. UTC | #19
On Sun, Jul 19, 2020 at 09:02:30PM +0200, Paul B Mahol wrote:
>> Yes, this is the non-recursive version, which is O(n) in the number of
>> samples. This is why I recommended the recursive version, which is O(log n)
>> in the number of samples and thus avoids the problem with big lengths.
>> It is fairly straightforward to convert one to the other.
> Really? How so?

As previously mentioned, you do two samples, then save to a temporary buffer,
two samples from that buffer with half the distance and so on until your
samples are only one pixel apart.

You can also try doing three or four samples at a time instead of two;
experiment a bit and see what gives the best performance.

/* Steinar */
Paul B Mahol July 19, 2020, 7:13 p.m. UTC | #20
On 7/19/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
> On Sun, Jul 19, 2020 at 09:02:30PM +0200, Paul B Mahol wrote:
>>> Yes, this is the non-recursive version, which is O(n) in the number of
>>> samples. This is why I recommended the recursive version, which is O(log
>>> n)
>>> in the number of samples and thus avoids the problem with big lengths.
>>> It is fairly straightforward to convert one to the other.
>> Really? How so?
>
> As previously mentioned, you do two samples, then save to a temporary
> buffer,
> two samples from that buffer with half the distance and so on until your
> samples are only one pixel apart.
>
> You can also try doing three or four samples at a time instead of two;
> experiment a bit and see what gives the best performance.

This is very slow.

>
> /* Steinar */
> --
> Homepage: https://www.sesse.net/
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol July 19, 2020, 7:50 p.m. UTC | #21
On 7/19/20, Paul B Mahol <onemda@gmail.com> wrote:
> On 7/19/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
>> On Sun, Jul 19, 2020 at 09:02:30PM +0200, Paul B Mahol wrote:
>>>> Yes, this is the non-recursive version, which is O(n) in the number of
>>>> samples. This is why I recommended the recursive version, which is
>>>> O(log
>>>> n)
>>>> in the number of samples and thus avoids the problem with big lengths.
>>>> It is fairly straightforward to convert one to the other.
>>> Really? How so?
>>
>> As previously mentioned, you do two samples, then save to a temporary
>> buffer,
>> two samples from that buffer with half the distance and so on until your
>> samples are only one pixel apart.
>>
>> You can also try doing three or four samples at a time instead of two;
>> experiment a bit and see what gives the best performance.
>
> This is very slow.
>

Also I fixed silly center artifacts in rblur implementation.

Speed here on historic CPU for hd720 is 12-13 frames per second for 23
fps video with slice threading enabled on 4 core CPU.

This could be made even faster at expense of more memory usage.
I really doubt there is faster algorithm that needs less memory but
with no quality drop.
Paul B Mahol July 21, 2020, 7:48 a.m. UTC | #22
On 7/19/20, Paul B Mahol <onemda@gmail.com> wrote:
> On 7/19/20, Paul B Mahol <onemda@gmail.com> wrote:
>> On 7/19/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
>>> On Sun, Jul 19, 2020 at 09:02:30PM +0200, Paul B Mahol wrote:
>>>>> Yes, this is the non-recursive version, which is O(n) in the number of
>>>>> samples. This is why I recommended the recursive version, which is
>>>>> O(log
>>>>> n)
>>>>> in the number of samples and thus avoids the problem with big lengths.
>>>>> It is fairly straightforward to convert one to the other.
>>>> Really? How so?
>>>
>>> As previously mentioned, you do two samples, then save to a temporary
>>> buffer,
>>> two samples from that buffer with half the distance and so on until your
>>> samples are only one pixel apart.
>>>
>>> You can also try doing three or four samples at a time instead of two;
>>> experiment a bit and see what gives the best performance.
>>
>> This is very slow.
>>
>
> Also I fixed silly center artifacts in rblur implementation.
>
> Speed here on historic CPU for hd720 is 12-13 frames per second for 23
> fps video with slice threading enabled on 4 core CPU.
>
> This could be made even faster at expense of more memory usage.
> I really doubt there is faster algorithm that needs less memory but
> with no quality drop.
>

Gonna apply this soon, as I'm happy with overall performance and output quality.
Nicolas George July 21, 2020, 8:25 a.m. UTC | #23
Paul B Mahol (12020-07-21):
> Gonna apply this soon, as I'm happy with overall performance and output quality.

Did you get another developer to approve it? If not, the block still
stands.
Paul B Mahol July 21, 2020, 8:38 a.m. UTC | #24
On 7/21/20, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12020-07-21):
>> Gonna apply this soon, as I'm happy with overall performance and output
>> quality.
>
> Did you get another developer to approve it? If not, the block still
> stands.

You have just approved it with reply to this same thread.

>
> --
>   Nicolas George
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol July 21, 2020, 10:59 a.m. UTC | #25
On 7/21/20, Paul B Mahol <onemda@gmail.com> wrote:
> On 7/21/20, Nicolas George <george@nsup.org> wrote:
>> Paul B Mahol (12020-07-21):
>>> Gonna apply this soon, as I'm happy with overall performance and output
>>> quality.
>>
>> Did you get another developer to approve it? If not, the block still
>> stands.
>
> You have just approved it with reply to this same thread.
>

Also it is new patch.

>>
>> --
>>   Nicolas George
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Nicolas George July 21, 2020, 12:41 p.m. UTC | #26
Paul B Mahol (12020-07-21):
> Also it is new patch.

All the more reason not to apply and wait for it to be reviewed and
tested. Fortunately, there is somebody both competent and interested
in the matter.
Steinar H. Gunderson July 21, 2020, 12:51 p.m. UTC | #27
On Tue, Jul 21, 2020 at 02:41:16PM +0200, Nicolas George wrote:
>> Also it is new patch.
> All the more reason not to apply and wait for it to be reviewed and
> tested. Fortunately, there is somebody both competent and interested
> in the matter.

I don't intend to look more at this; Paul has made it clear what his position
is, and I don't see the point in arguing further about it. Again, if someone
else wants to approve the patch in this state, I'm fine by that, but I'm not
doing it myself. (If a patch is good, it should be easy finding at least one
other developer that is willing to +1 it.)

/* Steinar */
Nicolas George July 21, 2020, 12:55 p.m. UTC | #28
Steinar H. Gunderson (12020-07-21):
> I don't intend to look more at this; Paul has made it clear what his position
> is, and I don't see the point in arguing further about it. Again, if someone
> else wants to approve the patch in this state, I'm fine by that, but I'm not
> doing it myself. (If a patch is good, it should be easy finding at least one
> other developer that is willing to +1 it.)

Thank you for weighing in.

So to summary, this patch is blocked pending approval by an independent
developer.

Regards,
Paul B Mahol July 21, 2020, 1:03 p.m. UTC | #29
On 7/21/20, Steinar H. Gunderson <steinar+ffmpeg@gunderson.no> wrote:
> On Tue, Jul 21, 2020 at 02:41:16PM +0200, Nicolas George wrote:
>>> Also it is new patch.
>> All the more reason not to apply and wait for it to be reviewed and
>> tested. Fortunately, there is somebody both competent and interested
>> in the matter.
>
> I don't intend to look more at this; Paul has made it clear what his
> position
> is, and I don't see the point in arguing further about it. Again, if someone
> else wants to approve the patch in this state, I'm fine by that, but I'm not
> doing it myself. (If a patch is good, it should be easy finding at least one
> other developer that is willing to +1 it.)

Could you write pseudo code of your O(1) algorithm for both radial and
circular blur?

If not, I see no point in trying to comply with imaginary standards.

>
> /* Steinar */
> --
> Homepage: https://www.sesse.net/
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

diff --git a/doc/filters.texi b/doc/filters.texi
index a2c31ff610..62705db984 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -7231,6 +7231,29 @@  Set planes to filter. Default value is to filter all
 planes except alpha plane.
 @end table
 
+@section cblur
+Apply a circular blur filter.
+
+The filter accepts the following options:
+
+@table @option
+@item centerx, centery
+Set central point position of circular blur. Default is @code{0.5}.
+
+@item amount
+Set amount of circular blur. Default is @code{0.03}.
+
+@item planes
+Set which planes to filter. By default all planes are filtered.
+@end table
+
+@subsection Commands
+This filter supports same @ref{commands} as options.
+The command accepts the same syntax of the corresponding option.
+
+If the specified expression is not valid, it is kept at its current
+value.
+
 @section chromahold
 Remove all color information for all colors except for certain one.
 
@@ -15768,6 +15791,29 @@  less than @code{0}, the filter will try to use a good random seed on a
 best effort basis.
 @end table
 
+@section rblur
+Apply a radial blur filter.
+
+The filter accepts the following options:
+
+@table @option
+@item centerx, centery
+Set central point position of radial blur. Default is @code{0.5}.
+
+@item amount
+Set amount of radial blur. Default is @code{0.03}.
+
+@item planes
+Set which planes to filter. By default all planes are filtered.
+@end table
+
+@subsection Commands
+This filter supports same @ref{commands} as options.
+The command accepts the same syntax of the corresponding option.
+
+If the specified expression is not valid, it is kept at its current
+value.
+
 @section readeia608
 
 Read closed captioning (EIA-608) information from the top lines of a video frame.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 33dcc69392..30f2abf9e2 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -180,6 +180,7 @@  OBJS-$(CONFIG_BOXBLUR_OPENCL_FILTER)         += vf_avgblur_opencl.o opencl.o \
                                                 opencl/avgblur.o boxblur.o
 OBJS-$(CONFIG_BWDIF_FILTER)                  += vf_bwdif.o yadif_common.o
 OBJS-$(CONFIG_CAS_FILTER)                    += vf_cas.o
+OBJS-$(CONFIG_CBLUR_FILTER)                  += vf_rblur.o
 OBJS-$(CONFIG_CHROMABER_VULKAN_FILTER)       += vf_chromaber_vulkan.o vulkan.o
 OBJS-$(CONFIG_CHROMAHOLD_FILTER)             += vf_chromakey.o
 OBJS-$(CONFIG_CHROMAKEY_FILTER)              += vf_chromakey.o
@@ -358,6 +359,7 @@  OBJS-$(CONFIG_PSNR_FILTER)                   += vf_psnr.o framesync.o
 OBJS-$(CONFIG_PULLUP_FILTER)                 += vf_pullup.o
 OBJS-$(CONFIG_QP_FILTER)                     += vf_qp.o
 OBJS-$(CONFIG_RANDOM_FILTER)                 += vf_random.o
+OBJS-$(CONFIG_RBLUR_FILTER)                  += vf_rblur.o
 OBJS-$(CONFIG_READEIA608_FILTER)             += vf_readeia608.o
 OBJS-$(CONFIG_READVITC_FILTER)               += vf_readvitc.o
 OBJS-$(CONFIG_REALTIME_FILTER)               += f_realtime.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index b896a76c9e..38279a44eb 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -171,6 +171,7 @@  extern AVFilter ff_vf_boxblur;
 extern AVFilter ff_vf_boxblur_opencl;
 extern AVFilter ff_vf_bwdif;
 extern AVFilter ff_vf_cas;
+extern AVFilter ff_vf_cblur;
 extern AVFilter ff_vf_chromahold;
 extern AVFilter ff_vf_chromakey;
 extern AVFilter ff_vf_chromanr;
@@ -341,6 +342,7 @@  extern AVFilter ff_vf_psnr;
 extern AVFilter ff_vf_pullup;
 extern AVFilter ff_vf_qp;
 extern AVFilter ff_vf_random;
+extern AVFilter ff_vf_rblur;
 extern AVFilter ff_vf_readeia608;
 extern AVFilter ff_vf_readvitc;
 extern AVFilter ff_vf_realtime;
diff --git a/libavfilter/vf_rblur.c b/libavfilter/vf_rblur.c
new file mode 100644
index 0000000000..080224bcf7
--- /dev/null
+++ b/libavfilter/vf_rblur.c
@@ -0,0 +1,560 @@ 
+/*
+ * Copyright (c) 2020 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct RBlurContext {
+    const AVClass *class;
+
+    float centerx, centery;
+    float amount;
+    int planes;
+    int cblur;
+    int center_x[4], center_y[4];
+
+    int depth;
+    int planewidth[4];
+    int planeheight[4];
+    int buffer_w[4], buffer_h[4];
+    float *buffer, *buffer_out;
+    int nb_planes;
+    int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
+} RBlurContext;
+
+typedef struct ThreadData {
+    AVFrame *in, *out;
+    int height;
+    int width;
+    int plane;
+} ThreadData;
+
+#define OFFSET(x) offsetof(RBlurContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
+
+static const AVOption rblur_options[] = {
+    { "centerx", "set center X position",  OFFSET(centerx), AV_OPT_TYPE_FLOAT, {.dbl=0.5},  0, 1,  FLAGS },
+    { "centery", "set center Y position",  OFFSET(centery), AV_OPT_TYPE_FLOAT, {.dbl=0.5},  0, 1,  FLAGS },
+    { "amount",  "set blur amount",        OFFSET(amount),  AV_OPT_TYPE_FLOAT, {.dbl=0.03}, 0, 1,  FLAGS },
+    { "planes",  "set planes to filter",   OFFSET(planes),  AV_OPT_TYPE_INT,   {.i64=0xF},  0,0xF, FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(rblur);
+
+static void cart_to_polar(float x, float y, float *r, float *a)
+{
+    *r = sqrtf(x * x + y * y);
+    *a = atan2f(y, x);
+}
+
+static void polar_to_cart(float r, float a, float *x, float *y)
+{
+    *x = r * cosf(a);
+    *y = r * sinf(a);
+}
+
+static inline int mod(int a, int b)
+{
+    const int res = a % b;
+
+    if (res < 0) {
+        return res + b;
+    } else {
+        return res;
+    }
+}
+
+static int filter_horizontally(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+    RBlurContext *s = ctx->priv;
+    ThreadData *td = arg;
+    const int height = td->height;
+    const int width = td->width;
+    const int slice_start = (height *  jobnr   ) / nb_jobs;
+    const int slice_end   = (height * (jobnr+1)) / nb_jobs;
+    const int size = s->amount * (width - 1);
+    float *buffer = s->buffer_out;
+    const float *src;
+    float *ptr;
+    int y;
+
+    /* Filter horizontally along each row */
+    for (y = slice_start; y < slice_end; y++) {
+        float acc = 0.f;
+        int count = 0;
+
+        src = (const float *)s->buffer + width * y;
+        ptr = buffer + width * y;
+
+        for (int i = 0; i <= size; i++) {
+            acc += src[mod(i, width)];
+            count++;
+        }
+
+        for (int i = 0; i < width; i++) {
+            ptr[mod(i + size / 2, width)] = acc / count;
+            acc += src[mod(i + size + 1, width)] - src[i];
+        }
+    }
+
+    return 0;
+}
+
+static int filter_vertically(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+    RBlurContext *s = ctx->priv;
+    ThreadData *td = arg;
+    const int height = td->height;
+    const int width = td->width;
+    const int slice_start = (width *  jobnr   ) / nb_jobs;
+    const int slice_end   = (width * (jobnr+1)) / nb_jobs;
+    const int size = s->amount * (height - 1);
+    float *buffer = s->buffer_out;
+    const float *src;
+    float *ptr;
+    int x;
+
+    /* Filter vertically along each column */
+    for (x = slice_start; x < slice_end; x++) {
+        int count = 0;
+        float acc = 0.f;
+
+        ptr = buffer + x;
+        src = s->buffer + x;
+
+        for (int i = 0; i <= size; i++) {
+            acc += src[mod(i, height) * width];
+            count++;
+        }
+
+        for (int i = 0; i < height; i++) {
+            ptr[mod(i + size / 2, height) * width] = acc / count;
+            acc += src[mod(i + size + 1, height) * width] - src[i * width];
+        }
+    }
+
+    return 0;
+}
+
+static int p2c(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+    RBlurContext *s = ctx->priv;
+    ThreadData *td = arg;
+    const int plane = td->plane;
+    const uint8_t *src = td->in->data[plane];
+    const uint16_t *src16 = (const uint16_t *)td->in->data[plane];
+    const int in_linesize = td->in->linesize[plane];
+    const int height = s->planeheight[plane];
+    const int width = s->planewidth[plane];
+    const int bheight = s->buffer_h[plane];
+    const int bwidth = s->buffer_w[plane];
+    const int center_x = s->center_x[plane];
+    const int center_y = s->center_y[plane];
+    const int slice_start = (bheight *  jobnr   ) / nb_jobs;
+    const int slice_end   = (bheight * (jobnr+1)) / nb_jobs;
+    float *bptr = s->buffer + slice_start * bwidth;
+
+    if (s->depth == 8) {
+        for (int y = slice_start; y < slice_end; y++) {
+            for (int x = 0; x < bwidth; x++) {
+                float fX, fY;
+                float u, v;
+                int iX, iY;
+                int X, Y;
+                float k[4];
+                int p[4];
+
+                polar_to_cart(x, (y / (bheight - 1.f)) * 2.f * M_PI - M_PI, &fX, &fY);
+                iX = floorf(fX);
+                iY = floorf(fY);
+                u = fX - iX;
+                v = fY - iY;
+                k[0] = (1.f - u) * (1.f - v);
+                k[1] = u * (1.f - v);
+                k[2] = (1.f - u) * v;
+                k[3] = u * v;
+                X = av_clip(iX + center_x, 0, width - 1);
+                Y = av_clip(iY + center_y, 0, height - 1);
+                p[0] = src[Y * in_linesize + X];
+                X = av_clip(iX + center_x + 1, 0, width - 1);
+                p[1] = src[Y * in_linesize + X];
+                X = av_clip(iX + center_x, 0, width - 1);
+                Y = av_clip(iY + center_y + 1, 0, height - 1);
+                p[2] = src[Y * in_linesize + X];
+                X = av_clip(iX + center_x + 1, 0, width - 1);
+                p[3] = src[Y * in_linesize + X];
+                bptr[x] = p[0] * k[0] + p[1] * k[1]  + p[2] * k[2] + p[3] * k[3];
+            }
+            bptr += bwidth;
+        }
+    } else {
+        for (int y = slice_start; y < slice_end; y++) {
+            for (int x = 0; x < bwidth; x++) {
+                float fX, fY;
+                float u, v;
+                int iX, iY;
+                int X, Y;
+                float k[4];
+                int p[4];
+
+                polar_to_cart(x, (y / (bheight - 1.f)) * 2.f * M_PI - M_PI, &fX, &fY);
+                iX = floorf(fX);
+                iY = floorf(fY);
+                u = fX - iX;
+                v = fY - iY;
+                k[0] = (1.f - u) * (1.f - v);
+                k[1] = u * (1.f - v);
+                k[2] = (1.f - u) * v;
+                k[3] = u * v;
+                X = av_clip(iX + center_x, 0, width - 1);
+                Y = av_clip(iY + center_y, 0, height - 1);
+                p[0] = src16[Y * in_linesize / 2 + X];
+                X = av_clip(iX + center_x + 1, 0, width - 1);
+                p[1] = src16[Y * in_linesize / 2 + X];
+                X = av_clip(iX + center_x, 0, width - 1);
+                Y = av_clip(iY + center_y + 1, 0, height - 1);
+                p[2] = src16[Y * in_linesize / 2 + X];
+                X = av_clip(iX + center_x + 1, 0, width - 1);
+                p[3] = src16[Y * in_linesize / 2 + X];
+                bptr[x] = p[0] * k[0] + p[1] * k[1]  + p[2] * k[2] + p[3] * k[3];
+            }
+            bptr += bwidth;
+        }
+    }
+
+    return 0;
+}
+
+static int c2p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+    RBlurContext *s = ctx->priv;
+    ThreadData *td = arg;
+    const int plane = td->plane;
+    const int out_linesize = td->out->linesize[plane];
+    const int height = s->planeheight[plane];
+    const int width = s->planewidth[plane];
+    const int bheight = s->buffer_h[plane];
+    const int bwidth = s->buffer_w[plane];
+    const int center_x = s->center_x[plane];
+    const int center_y = s->center_y[plane];
+    const int slice_start = (height *  jobnr   ) / nb_jobs;
+    const int slice_end   = (height * (jobnr+1)) / nb_jobs;
+    uint8_t *dst = td->out->data[plane] + slice_start * out_linesize;
+    uint16_t *dst16 = (uint16_t *)td->out->data[plane] + slice_start * out_linesize / 2;
+    float *bptr = s->buffer_out;
+
+    if (s->depth == 8) {
+        for (int y = slice_start; y < slice_end; y++) {
+            for (int x = 0; x < width; x++) {
+                float fX, fY;
+                float u, v;
+                int iX, iY;
+                int X, Y;
+                float k[4];
+                int p[4];
+
+                cart_to_polar(x - center_x, y - center_y, &fX, &fY);
+                iX = floorf(fX);
+                fY = ((fY + M_PI) / (2.f * M_PI)) * (bheight - 1);
+                iY = floorf(fY);
+                u = fX - iX;
+                v = fY - iY;
+                k[0] = (1.f - u) * (1.f - v);
+                k[1] = u * (1.f - v);
+                k[2] = (1.f - u) * v;
+                k[3] = u * v;
+                X = av_clip(iX, 0, bwidth - 1);
+                Y = av_clip(iY, 0, bheight - 1);
+                p[0] = bptr[Y * bwidth + X];
+                X = av_clip(iX + 1, 0, bwidth - 1);
+                Y = av_clip(iY, 0, bheight - 1);
+                p[1] = bptr[Y * bwidth + X];
+                X = av_clip(iX, 0, bwidth - 1);
+                Y = av_clip(iY + 1, 0, bheight - 1);
+                p[2] = bptr[Y * bwidth + X];
+                X = av_clip(iX + 1, 0, bwidth - 1);
+                Y = av_clip(iY + 1, 0, bheight - 1);
+                p[3] = bptr[Y * bwidth + X];
+                dst[x] = p[0] * k[0] + p[1] * k[1]  + p[2] * k[2] + p[3] * k[3];
+            }
+            dst += out_linesize;
+        }
+    } else {
+        for (int y = slice_start; y < slice_end; y++) {
+            for (int x = 0; x < width; x++) {
+                float fX, fY;
+                float u, v;
+                int iX, iY;
+                int X, Y;
+                float k[4];
+                int p[4];
+
+                cart_to_polar(x - center_x, y - center_y, &fX, &fY);
+                iX = floorf(fX);
+                fY = ((fY + M_PI) / (2.f * M_PI)) * (bheight - 1);
+                iY = floorf(fY);
+                u = fX - iX;
+                v = fY - iY;
+                k[0] = (1.f - u) * (1.f - v);
+                k[1] = u * (1.f - v);
+                k[2] = (1.f - u) * v;
+                k[3] = u * v;
+                X = av_clip(iX, 0, bwidth - 1);
+                Y = av_clip(iY, 0, bheight - 1);
+                p[0] = bptr[Y * bwidth + X];
+                X = av_clip(iX + 1, 0, bwidth - 1);
+                Y = av_clip(iY, 0, bheight - 1);
+                p[1] = bptr[Y * bwidth + X];
+                X = av_clip(iX, 0, bwidth - 1);
+                Y = av_clip(iY + 1, 0, bheight - 1);
+                p[2] = bptr[Y * bwidth + X];
+                X = av_clip(iX + 1, 0, bwidth - 1);
+                Y = av_clip(iY + 1, 0, bheight - 1);
+                p[3] = bptr[Y * bwidth + X];
+                dst16[x] = p[0] * k[0] + p[1] * k[1]  + p[2] * k[2] + p[3] * k[3];
+            }
+            dst16 += out_linesize / 2;
+        }
+    }
+
+    return 0;
+}
+
+static void polar2cart(AVFilterContext *ctx, AVFrame *in, int plane)
+{
+    RBlurContext *s = ctx->priv;
+    const int nb_threads = ff_filter_get_nb_threads(ctx);
+    ThreadData td;
+
+    td.plane = plane;
+    td.in = in;
+
+    ctx->internal->execute(ctx, p2c, &td, NULL, FFMIN(s->buffer_h[plane], nb_threads));
+}
+
+static void cart2polar(AVFilterContext *ctx, AVFrame *out, int plane)
+{
+    RBlurContext *s = ctx->priv;
+    const int nb_threads = ff_filter_get_nb_threads(ctx);
+    ThreadData td;
+
+    td.plane = plane;
+    td.out = out;
+
+    ctx->internal->execute(ctx, c2p, &td, NULL, FFMIN(s->planeheight[plane], nb_threads));
+}
+
+static void ririir2d(AVFilterContext *ctx, int plane)
+{
+    RBlurContext *s = ctx->priv;
+    const int nb_threads = ff_filter_get_nb_threads(ctx);
+    ThreadData td;
+
+    td.width = s->buffer_w[plane];
+    td.height = s->buffer_h[plane];
+
+    ctx->internal->execute(ctx, s->filter, &td, NULL, FFMIN(td.height, nb_threads));
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    static const enum AVPixelFormat pix_fmts[] = {
+        AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
+        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
+        AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
+        AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
+        AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
+        AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
+        AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
+        AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
+        AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
+        AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
+        AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
+        AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
+        AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
+        AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
+        AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
+        AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
+        AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
+        AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
+        AV_PIX_FMT_NONE
+    };
+
+    return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
+}
+
+static void set_params(AVFilterContext *ctx)
+{
+    RBlurContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+
+    s->center_x[1] = s->center_x[2] = AV_CEIL_RSHIFT(lrintf(inlink->w * s->centerx), desc->log2_chroma_w);
+    s->center_x[0] = s->center_x[3] = lrintf(inlink->w * s->centerx);
+    s->center_y[1] = s->center_y[2] = AV_CEIL_RSHIFT(lrintf(inlink->h * s->centery), desc->log2_chroma_h);
+    s->center_y[0] = s->center_y[3] = lrintf(inlink->h * s->centery);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+    RBlurContext *s = ctx->priv;
+
+    s->cblur = !strcmp(ctx->filter->name, "cblur");
+
+    set_params(ctx);
+    s->depth = desc->comp[0].depth;
+    s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
+    s->planewidth[0] = s->planewidth[3] = inlink->w;
+    s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
+    s->planeheight[0] = s->planeheight[3] = inlink->h;
+
+    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
+    s->buffer_w[0] = ceilf(hypotf(s->planewidth[0], s->planeheight[0]) / 2.f);
+    s->buffer_w[1] = ceilf(hypotf(s->planewidth[1], s->planeheight[1]) / 2.f);
+    s->buffer_w[2] = ceilf(hypotf(s->planewidth[2], s->planeheight[2]) / 2.f);
+    s->buffer_w[3] = ceilf(hypotf(s->planewidth[3], s->planeheight[3]) / 2.f);
+    s->buffer_h[0] = s->planewidth[0] + s->planeheight[0];
+    s->buffer_h[1] = s->planewidth[1] + s->planeheight[1];
+    s->buffer_h[2] = s->planewidth[2] + s->planeheight[2];
+    s->buffer_h[3] = s->planewidth[3] + s->planeheight[3];
+
+    s->buffer = av_malloc_array(s->buffer_w[0], s->buffer_h[0] * sizeof(*s->buffer));
+    s->buffer_out = av_malloc_array(s->buffer_w[0], s->buffer_h[0] * sizeof(*s->buffer_out));
+    if (!s->buffer || !s->buffer_out)
+        return AVERROR(ENOMEM);
+
+    s->filter = s->cblur ? filter_vertically : filter_horizontally;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx = inlink->dst;
+    RBlurContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    AVFrame *out;
+    int plane;
+
+    if (av_frame_is_writable(in)) {
+        out = in;
+    } else {
+        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+        if (!out) {
+            av_frame_free(&in);
+            return AVERROR(ENOMEM);
+        }
+        av_frame_copy_props(out, in);
+    }
+
+    set_params(ctx);
+
+    for (plane = 0; plane < s->nb_planes; plane++) {
+        const int height = s->planeheight[plane];
+        const int width = s->planewidth[plane];
+
+        if (!(s->planes & (1 << plane))) {
+            if (out != in)
+                av_image_copy_plane(out->data[plane], out->linesize[plane],
+                                    in->data[plane], in->linesize[plane],
+                                    width * ((s->depth + 7) / 8), height);
+            continue;
+        }
+
+        polar2cart(ctx, in, plane);
+        ririir2d(ctx, plane);
+        cart2polar(ctx, out, plane);
+    }
+
+    if (out != in)
+        av_frame_free(&in);
+    return ff_filter_frame(outlink, out);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    RBlurContext *s = ctx->priv;
+
+    av_freep(&s->buffer);
+    av_freep(&s->buffer_out);
+}
+
+static const AVFilterPad rblur_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_input,
+        .filter_frame = filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad rblur_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+    },
+    { NULL }
+};
+
+#if CONFIG_RBLUR_FILTER
+
+AVFilter ff_vf_rblur = {
+    .name          = "rblur",
+    .description   = NULL_IF_CONFIG_SMALL("Apply Radial Blur filter."),
+    .priv_size     = sizeof(RBlurContext),
+    .priv_class    = &rblur_class,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .inputs        = rblur_inputs,
+    .outputs       = rblur_outputs,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .process_command = ff_filter_process_command,
+};
+
+#endif /* CONFIG_RBLUR_FILTER */
+
+#if CONFIG_CBLUR_FILTER
+
+#define cblur_options rblur_options
+AVFILTER_DEFINE_CLASS(cblur);
+
+AVFilter ff_vf_cblur = {
+    .name          = "cblur",
+    .description   = NULL_IF_CONFIG_SMALL("Apply Circular Blur filter."),
+    .priv_size     = sizeof(RBlurContext),
+    .priv_class    = &cblur_class,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .inputs        = rblur_inputs,
+    .outputs       = rblur_outputs,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .process_command = ff_filter_process_command,
+};
+
+#endif /* CONFIG_CBLUR_FILTER */