PR #25309 opened by Marti2203 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25309 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25309.patch
`reflecty()` in vf_v360.c maps a row below the bottom edge with `2 * h - 1 - y`, so row h becomes row h - 1, but a row above the top edge with `y = -y`, so row -1 becomes row 1 and row 0 is skipped. The interpolators with a 4x4 window (cubic, lanczos, spline16, gaussian, mitchell) sample row `vi - 1`, so near the top pole they read the wrong row, while the bottom pole is handled correctly. For an equirect input that is symmetric about the equator, the output is therefore not symmetric: ``` # 128x64 gray equirect: rows 0 and 63 = 255, rows 1 and 62 = 0, rest 128 ffmpeg -f rawvideo -pix_fmt gray -s 128x64 -i in.gray -vf v360=e:e:interp=cubic:w=128:h=100 -f rawvideo out.gray ``` (row r, row h-1-r) of the output for r = 0..2, and the largest difference between the output and its vertical flip: ``` master cubic [(187, 172), (12, 9), (59, 59)] max diff 16 master lanczos [(200, 178), (5, 5), (63, 63)] max diff 22 master spline16 [(194, 174), (9, 7), (58, 58)] max diff 20 patched cubic [(172, 172), (9, 9), (59, 59)] max diff 1 patched lanczos [(178, 178), (5, 5), (63, 63)] max diff 1 patched spline16 [(174, 174), (7, 7), (58, 58)] max diff 1 ``` Linear interpolation is not affected (max diff 0 before and after). The patch reflects row -1 to row 0 (`y = -y - 1`), matching the bottom edge. I also checked the horizontal helpers: `ereflectx()` shifts by half the width at both poles and `reflectx()` mirrors with `w - 1 - x`, both symmetric, and a left-right symmetric input gives a left-right symmetric output (max diff 1) on master already, so they are left alone. `reflecty()` is also used by the tetrahedron and tspyramid inputs, which get the same correction. The second commit adds `fate-filter-v360-reflecty`: a vertically symmetric frame (geq pattern stacked with its vflip) goes through `v360=e:e:interp=cubic`, and the test outputs the difference between the result and its vertical flip, thresholded at 2. The threshold absorbs the ±1 from the float coordinates, so the expected output is a black frame and does not depend on libm details; on master 232 pixels are above it. The test fails on master and passes with the fix (THREADS=1 and 4, gcc and clang builds); `make fate-vfilter` (503 tests without samples) passes. There was no FATE coverage for v360 before. I could not test the x86 SIMD remap functions (no nasm here), but they only consume the index tables this patch changes. This issue was found by an LLM-Enhanced Static Analysis Engine and verified manually. 🤖 Generated with [Claude Code](https://claude.com/claude-code) From 97b520ed2808750516b59c60ee2f20dd1a6dc19d Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Wed, 21 Oct 2026 19:08:52 +0800 Subject: [PATCH 1/2] avfilter/vf_v360: fix reflecty() at the top edge reflecty() maps row h to h - 1 at the bottom edge but row -1 to row 1 at the top edge, skipping row 0. Interpolators with a 4x4 window (cubic, lanczos, spline16, gaussian, mitchell) sample row -1 near the top pole, so they read the wrong row there, and a vertically symmetric equirect input gives an output that is not vertically symmetric. Reflect row -1 to row 0, like the bottom edge. ereflectx() and reflectx() are already symmetric. Signed-off-by: Martin Mirchev <[email protected]> --- libavfilter/vf_v360.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavfilter/vf_v360.c b/libavfilter/vf_v360.c index 367c217a799..b7671c5a6bf 100644 --- a/libavfilter/vf_v360.c +++ b/libavfilter/vf_v360.c @@ -771,7 +771,7 @@ static inline int mod(int a, int b) static inline int reflecty(int y, int h) { if (y < 0) { - y = -y; + y = -y - 1; } else if (y >= h) { y = 2 * h - 1 - y; } -- 2.54.0 From a240f261e99f47753acf89422bf37d4dc97429b9 Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Wed, 21 Oct 2026 19:09:09 +0800 Subject: [PATCH 2/2] tests/fate/filter-video: add a test for v360 reflection at the poles Feed a vertically symmetric equirect frame through v360 with cubic interpolation and output the difference between the result and its vertical flip, thresholded at 2 to absorb the rounding of the float coordinates. The output must be black; before the previous commit the rows near the top pole differed from the bottom ones. Signed-off-by: Martin Mirchev <[email protected]> --- tests/fate/filter-video.mak | 4 ++++ tests/filtergraphs/v360-reflecty | 5 +++++ tests/ref/fate/filter-v360-reflecty | 6 ++++++ 3 files changed, 15 insertions(+) create mode 100644 tests/filtergraphs/v360-reflecty create mode 100644 tests/ref/fate/filter-v360-reflecty diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak index 4a065ec0320..dc4240a6605 100644 --- a/tests/fate/filter-video.mak +++ b/tests/fate/filter-video.mak @@ -430,6 +430,10 @@ fate-filter-untile: CMD = framecrc -lavfi testsrc2=d=1:r=2,untile=2x2 FATE_FILTER-$(call FILTERFRAMECRC, TESTSRC2 UNTILE) += fate-filter-untile-yuv422p fate-filter-untile-yuv422p: CMD = framecrc -lavfi testsrc2=d=1:r=2,format=yuv422p,untile=2x2 +FATE_FILTER-$(call FILTERFRAMECRC, NULLSRC FORMAT GEQ SPLIT VFLIP VSTACK V360 BLEND LUT, FILE_PROTOCOL) += fate-filter-v360-reflecty +fate-filter-v360-reflecty: tests/data/filtergraphs/v360-reflecty +fate-filter-v360-reflecty: CMD = framecrc -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/v360-reflecty + FATE_FILTER_VSYNTH_PGMYUV-$(CONFIG_UNSHARP_FILTER) += fate-filter-unsharp fate-filter-unsharp: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf unsharp=11:11:-1.5:11:11:-1.5 diff --git a/tests/filtergraphs/v360-reflecty b/tests/filtergraphs/v360-reflecty new file mode 100644 index 00000000000..959dd0d4307 --- /dev/null +++ b/tests/filtergraphs/v360-reflecty @@ -0,0 +1,5 @@ +nullsrc=s=128x32:r=1:d=1, format=gray, geq=lum='mod(X*37+Y*101,256)', split [top][b]; +[b] vflip [bottom]; +[top][bottom] vstack, v360=e:e:interp=cubic:w=128:h=100, split [out][o]; +[o] vflip [flipped]; +[out][flipped] blend=all_mode=difference, lut=c0='gt(val,2)*255' diff --git a/tests/ref/fate/filter-v360-reflecty b/tests/ref/fate/filter-v360-reflecty new file mode 100644 index 00000000000..40acda689a5 --- /dev/null +++ b/tests/ref/fate/filter-v360-reflecty @@ -0,0 +1,6 @@ +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 128x100 +#sar 0: 1/1 +0, 0, 0, 1, 12800, 0x00000000 -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
