[FFmpeg-trac] #9781(avfilter:closed): zscale adds distortion when resampling video data
FFmpeg
trac at avcodec.org
Fri May 13 21:16:49 EEST 2022
#9781: zscale adds distortion when resampling video data
-------------------------------------+-------------------------------------
Reporter: Llyw | Owner: (none)
Type: defect | Status: closed
Priority: normal | Component: avfilter
Version: git-master | Resolution: fixed
Keywords: zscale | Blocked By:
distortion |
Blocking: | Reproduced by developer: 0
Analyzed by developer: 0 |
-------------------------------------+-------------------------------------
Comment (by Llyw):
That's an easy question to ask, but a difficult one to answer ;)
While I won't be able to answer your question to the full extent, I tried
to gain a rough understanding on how the use of multiple slices might
cause floating-point deviations: Internally, z.lib/zimg seems to convert
from the input to the output coordinate system, see https://github.com
/sekrit-twc/zimg/blob/release-3.0.4/src/zimg/resize/filter.cpp#L250, while
the parallelization scheme is doing it the other way around. Since `oh/iw`
is not necessarily equal to `1/(oh/ih)` in floating-point arithmetics that
could lead to some minor deviations due to round-off. In relative terms,
this deviation should be in the same order as the unit round-off for
double precision floating-point arithmetic, so I do not think that this
should be a reason to reopen this defect. I furthermore suspect that there
might be no way around a minor loss of accuracy when parallelizing the
execution of the `zscale` filter.
It might make sense, however, to ask the question whether you will get
identical results when using only a single slice. As `ih` and `oh`
approach `INT_MAX` (not that this will happen any time soon) you could
improve the following assignment, at least from a theoretical/academic
point of view:
{{{
s->in_slice_start[i] = s->out_slice_start[i] * in_h /
(double)out_h;
s->in_slice_end[i] = s->out_slice_end[i] * in_h /
(double)out_h;
}}}
When rewriting this as
{{{
s->in_slice_start[i] = in_h * (s->out_slice_start[i] /
(double)out_h);
s->in_slice_end[i] = in_h * (s->out_slice_end[i] /
(double)out_h);
}}}
you can be certain (not withstanding my comment in the final paragraph
down below) that this will reduce precisely to
{{{
s->in_slice_start[0] = 0.0;
s->in_slice_end[0] = in_h;
}}}
for a single slice since as in this case you have
`s->out_slice_start[0]=0.0` and `s->out_slice_end[0]=(double)oh` with `oh`
being equal to `out_h`: Since the dimensions of an AVFrame are 32-bit
signed integers, see https://ffmpeg.org/doxygen/2.7/structAVFrame.html,
and 32-bit signed integers can be exactly represented as double precision
floating-point values, you can be certain that the expression
`s->out_slice_end[0]/(double)out_h` will be equal to `1.0`, causing the
endpoint of your slice to be exactly equal to `in_h`, which is also the
default that `z.lib/zimg` assumes when the `active_area` member is not
specified, see https://github.com/sekrit-
twc/zimg/blob/release-3.0.4/src/zimg/api/zimg.cpp#L405, as was the case
before the Intel-commit.
Note, however, the default floating-point model will give the compiler
some freedom when it comes to interpreting the source code and I am not
sure whether the above reasoning will hold in general. If you want to
investigate this further, I would recommend that you add some regression
tests.
I should also point out that I have not yet had the opportunity to try my
reproducer commands with the updated code and even then I will be limited
to a simple visual comparison. I strongly expect that I will not be able
to tell a difference with my yuv420p video (when comparing the updated
git-master to FFmpeg 5.0.1) when I get around to doing that, but will get
back to you if this turns out not to be the case.
--
Ticket URL: <https://trac.ffmpeg.org/ticket/9781#comment:13>
FFmpeg <https://ffmpeg.org>
FFmpeg issue tracker
More information about the FFmpeg-trac
mailing list