View Full Version : neo-mv (mvtools independent reimplementation)


MeteorRain
23rd September 2026, 21:31
neo-mv is an independent reimplementation of mvtools/MVUtensils project.

https://github.com/HomeOfAviSynthPlusEvolution/neo-mv

- Disclaimer: This project is written from scratch by AI under my supervision and steering
- Modern C++ 17
- Google Highway SIMD (SSE2/AVX2/AVX512/AVX10/Neon/SVE)
- Windows / Linux + amd64 / arm64
- Based on DualSynth2 framework, supporting both AVS+ and VS
- PocketFFT instead of FFTW3 backend
- New prefixed functions / namespace not to conflict with existing plugins
- Automated full black-box testing coverage against MVUtensils project

The intention of this project is to modernize mvtools, and recreate the project by rewriting everything from scratch. It provides an alternative to the original mvtools plugin, and also helps to cross validate the correctness of the implementation.

Performance wise, it's on-par with MVUtensils, with some of the scenarios slower, some faster.

It's a drop-in replacement of MVUtensils.

It's NOT a drop-in replacement of AVS mvtools. You must opt-in and adopt the new APIs.

Results are not bit-identical, and can introduce minor difference from rounding error.

MysteryX
23rd September 2026, 23:51
Is that just for AviSynth or both AviSynth and VapourSynth implementation? Plugins can be compiled to support both interfaces at once.

Does it support blksize 6, 12, 24, 48? That's my main issue porting FrameRateConverter properly from AVS to VapourSynth -- only the AVS version supports those block sizes.

Also some DCT modes are missing from mvutensils

(if you're gonnna rewrite it, might as well refill the gaps)

MeteorRain
24th September 2026, 00:30
Refilling the gaps is not an issue. Feel free to request features. Yes, DS2 plugins support both interfaces with 1 binary.

MeteorRain
24th September 2026, 01:55
lol the test suites found out a compiler bug that miscompiles NEON code on ARM64. Need to wait for LLVM to fix some bugs.

MysteryX
24th September 2026, 04:54
That could take a while. Wouldn't it be easier for Astra to just hack into the LLVM devs machines, fix it and publish it on its own?

MeteorRain
24th September 2026, 05:08
Astra already did a hack to force compiler to not generate that wrong code.
Initial release (0.9.0) available at https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/releases/tag/0.9.0

MeteorRain
24th September 2026, 09:36
neo-mv 0.9.1
Added support for 6x6 (SAD only), 12x12, 24x24, and 48x48 block sizes.

DTL
24th September 2026, 10:24
Feel free to request features.

Here is a list of about 50 new features (changes) to mvtools post version 2.7.45 - https://github.com/DTL2020/mvtools/blob/mvtools-pfmod/new_features_list.ods

Some are already implemented in that repository and you can point AI tools to extract.

Also it is good to provide common working scripts like QTGMC and SMDegrain new versions for new builds to the users of new builds for quick testing. Because new builds uses different API and require manual redesign of the old scripts.

Some good modern (future) features to support: Hexagonal sampling lattice in addition to standard industry Cartesian and where possible - partial plane processing (in selected filters).

Hexagonal lattice as google AI promises support about 13% more spatial data (in the same byte buffer) and can be more memory efficient (performance) with the comparable quality. But its support require about complete new set of dissimilarity computing SIMD functions and compensation functions.

MeteorRain
24th September 2026, 10:28
Also it is good to provide common working scripts like QTGMC and SMDegrain to the users of new builds for quick testing. Because new builds uses different API and require manual redesign of the old scripts.

Can't do that now because there are more to come before I can start porting QTGMC and SMDegrain etc.

Will start with new features once everything settles down.

DTL
24th September 2026, 10:44
There was an idea that the great AI tools can make QTGMC port to the new engine in a second (and keep updated with new features as they are added) ? It is expected to be much easier in comparison with working binary creation.

With AI development tools it may be more user friendly to create a final working solution with each project update in comparison with creating only a new API (complex part) but leaving users waiting for someone to integrate this API to real production scripts (expected very simple but still time consuming part).
As mvtools is not a final working product but only an execution engine to be used in scripts, maybe it is possible to add to the general update project the generally used scripts auto-updates too ?

MeteorRain
24th September 2026, 11:44
Even with AI it'll take some time. Let's say, the next thing on my roadmap would be an FFT related plugin. Please allow some time.

AVIL
24th September 2026, 12:12
Hi all

A humbly request. Could be possible implement a especial mode of SAD. In this mode, block SAD is not increased for a pair of pixels when any of the two have the exact value zero. Standard SAD otherwise. This can be used to mask certain stubborn pixels or work in the borders of letterboxed clips.

DTL
25th September 2026, 11:06
In the narrow range mapping with 8bit encoding black is code value of 16. Not zero as in full range RGB. mvtools users for performance mostly compute SAD in YV12 so black code values are 16 for Y and 128 (127 ?) for UV planes (if chroma planes enabled for quality).

If the user wants to mark some blocks as zero SAD (or SAD not used in some next level of computing like full frame or full area SAD ?) it is better to enter key code values as filter parameters instead of zero code value (only working for full range RGB rarely used).

But it will also skip any other blocks of SAD computing (not only borders but any other areas of the frame) if standard black code values exist and can cause other issues.

Also some feature for performance: Add support for interleaved RGB (RGB24 and others) and interleaved YUV (4:4:4) if not yet present (mostly in MAnaylse/Analyse/AnalyseMany). It expected to be more memory friendly in comparison with separated planes fetching even with YV12 compressed format. With separated planes each SAD computing with chroma require 3 separate memory stream to be fetched from host RAM (if frame is big enough) and auto hardware prefetchers and memory transfer bulk logic make transfer in big blocks of linear addresses (much larger in comparison with 8x8 8bit block row size of 8 bytes). This fills caches and busses with redundant traffic if no more surround (or right positioned) blocks will be processed (like with natural noised MVs fields). With interleaved formats only 1 memory read stream per 1 colour block line is required.

Also ME (and MC) in 4:4:4 format makes better precision. See issue https://github.com/pinterf/mvtools/issues/59 . Old mvtools make very simple and low quality point-like spatial interpolation of chroma positions in chroma-subsampled formats in both ME and MC actions.

Better handling (higher quality) for chroma-subsampled formats also good new feature for new mvtools (require 1/8 intersample distance spatial granularity precomputing or runtime shifting of UV planes for 4:2:0 formats with pel=4 for Y plane).

MeteorRain
25th September 2026, 12:36
444 ME/MC would require a big change. For now converting to 444 and back would probably be more practical.

DTL
25th September 2026, 15:46
If processing in 4:2:0 the UV planes in super-clip need +1 level finer granulation interpolation. For pel=1 UV planes need to be interpolated to pel=2 size and for pel=2 UV planes need to be interpolated to pel=4 size and for pel=4 UV planes need to be interpolated to pel=8 size. It is very close to upsample UVs to 4:4:4 processing.

That mean with any input chroma-subsampling format the block start address fetching function GetPointer https://github.com/pinterf/mvtools/blob/cdf04a5edae37b8891bb46e07593be8c3cca0868/Sources/MDegrainN.cpp#L1953 can operate with full MVs precision instead of integer division by 2 for UV planes in chroma subsampled formats and loss precision (and also with possible asymmetrical chroma shift without proper rounder addition).

I have not yet looked at how this is designed in neo-mv sources.

MeteorRain
26th September 2026, 05:33
neo-mv 0.9.3

Now support modes from avs mvtools

0 → metric="sad"
1 → metric="dct"
2 → metric="sad_dct_global"
3 → metric="sad_dct_local", metric_weight=0.5, metric_threshold=0.03125
4 → metric="sad_dct_local", metric_weight=0.75, metric_threshold=0.03125
5 → metric="satd"
6 → metric="sad_satd_global"
7 → metric="sad_satd_local", metric_weight=0.5, metric_threshold=0.03125
8 → metric="sad_satd_local", metric_weight=0.75, metric_threshold=0.03125
9 → metric="sad_satd_global_half"
10 → metric="sad_satd_local", metric_weight=0.25, metric_threshold=0.0625

DTL
26th September 2026, 08:59
The second solution to fix precision lost at the chroma subsampled formats processing (and also some performance gain) is to switch from sub-shift precomputing in super clip to runtime sub-shift computing for requested blocks.

It already implemented in the https://github.com/DTL2020/mvtools/blob/9eedb9d0850f638fc43212fb515cf048f9b9a58f/Sources/MDegrainN.cpp#L3726 . Implementation for chroma block https://github.com/DTL2020/mvtools/blob/9eedb9d0850f638fc43212fb515cf048f9b9a58f/Sources/MVPlane.cpp#L598
Expected coeffs for 1/8 Wiener shifting - https://github.com/DTL2020/mvtools/blob/9eedb9d0850f638fc43212fb515cf048f9b9a58f/Sources/MVPlane.cpp#L130

Its performance at AVX2 CPU and MC filters (like MDegrain) is already better in comparison with precomputing because of a better memory performance and also much less memory required for super clip frames storage.

1. MC filters only request 1 sub-shifted position maximum for 1 block of image (runtime sub-shifting computing is already faster even at AVX2 chip). Some MVs (zero fractional parts of dx and dy) do not need sub-shifting at all (probability about 1/4 for pel=2 and about 1/16 for pel=4 and may be higher for true static areas).
2. ME onCPU filters may request up to all possible sub-shifted positions for each input image block depending on search algorithm (ESA request all) and performance depends on the search used.

Performance of the sub-shift function mostly depends on the compute performance of the CPU core and is expected to be better for AVX512 and future chips. The working set for this function is very small and fits in L1D cache (or even in register file). It is only 1x blocksize +padding of surrounding 2 samples max for best quality Wiener interpolation (currently available in mvtools).

Performance of the runtime sub-shifting filter fixed for any sub-shift distance because only coeffs changed but number of computing FMAs is equal (for each filter type - for bicubic with 4 samples it is even better in comparison with Wiener with 6 samples). So it can support any sub-shift distance with the same performance and 1/8 steps for 4:2:0 formats and pel=4.

The runtime sub-shifting is better for hardware or other off-CPU ME use cases where super clip with precomputed sub-shifted positions is not used at all (hardware ME engines do anything inside with much better performance in comparison with CPU).

MysteryX
26th September 2026, 09:04
Nice. After so many years, finally a proper implementation that allows writing with all features between AviSynth and VapourSynth.

MeteorRain
26th September 2026, 12:27
@DTL - I'll look into that in near future. Should be possible to defer to astra for implementation as long as spec is clear.

@MysteryX - Do you know if there are anything else that need to be ported into the new plugin? If people are happy about it I'd probably release 1.0.0 soon.

MysteryX
26th September 2026, 13:35
Removed filters / arguments (named as gone)
- BlockFPS — gone. FPS conversion is FlowFPS only.
- Finest — gone. Super’s pel planes are used directly; no cache helper clip.
- Flow(mode=…) — Flow still exists, but mode is gone. Only the old mode=0 path remains.
- search_coarse — gone. Coarse levels use the same search as the fine level.
- divide — gone. No 4-way block split after analysis.
- truemotion — gone as a switch. Defaults were flattened (lsad=400, pnew=25, etc.), not the old TrueMotion preset pair.
- scbehavior — gone.

DCT collapsed to a boolean

Classic / Pinterf: dct=0…10 (SAD, several DCT/SATD mixes, FFTW modes).
MVUtensils: satd=False ≡ old dct=0, satd=True ≡ old dct=5.
That is the whole map. Modes 1–4 and 6–10 (the FFTW-backed ones especially) are not there. That is the “some DCT modes are missing” line you already posted, stated as an API cut rather than an unfinished port.

Search / pyramid cuts
- Old search / pelsearch 0–7 → 0–5. Old modes 0 and 1 dropped; remaining values shifted by −2.
- rfilter 2/4 → 1/2. Old modes 1 and 3 dropped.

AVS / Pinterf surface that never existed on this line

These were never in the VS port MVUtensils came from, so they are also absent:
- MScaleVect / MStoreVect / MRestoreVect (Pinterf’s vector scale / persist path).
- outfile, temporal, multi, sadx264, scaleCSAD.
- MShow (already missing in dubhater; people used manipmv instead).
- YUY2 / interleaved packed formats.
- AVSTP internal MT.
- DTL’s post-2.7.45 pile (hex lattice, HW ME, extra MDegrain knobs, etc.) — that is a different fork, not Pinterf classic, and not MVUtensils.


and here's another report

Definitely missing functionality

Vector inspection and manipulation

- `MShow` / `MVShow` — drawing vector arrows, SAD values, block numbers, tolerance filtering, etc. MVUtensils has masks, but no comparable vector-field visualizer. The older VapourSynth port also omitted this and points users toward `vapoursynth-manipmv`. :chatgpt-content-reference{index="0"}
- `MMask` kinds 3–5:
- horizontal-vector component
- vertical-vector component
- UV vector colormap
MVUtensils implements only the old kinds 0–2 as `VectorLengthMask`, `SADMask`, and `OcclusionMask`. :chatgpt-content-reference{index="1"}
- `MScaleVect` — spatially scaling vectors/block geometry, scaling only vector components, flipping forward/backward direction, adjusting subpel metadata, and changing reported analysis bit depth. Some bit-depth use cases are unnecessary in MVUtensils because its consumers can reuse vectors across bit depths, but spatial scaling and flipping have no equivalent.
- `MStoreVect` / `MRestoreVect` — packaging vectors into an ordinary losslessly encodable clip and restoring them later for two-pass processing.
- `Finest` — exposing the finest/subpixel level of a super clip to scripts. MVUtensils uses that level internally but does not expose it as a filter. `Finest` was available in the original VapourSynth port. :chatgpt-content-reference{index="2"}

Motion analysis controls

- OneTimeSearch and NStepSearch. MVUtensils’ search modes correspond to legacy modes 2–7; legacy modes 0 and 1 are absent. Pinterf documents all eight search algorithms. :chatgpt-content-reference{index="3"}
- Independent coarse-level search mode through `search_coarse`.
- `divide`, which subdivides the resulting vector field into smaller blocks.
- Temporal prediction in `MAnalyse` through `temporal=true`.
- Chroma-SAD weighting through `scaleCSAD`.
- Vector-file export through `outfile` in both `MAnalyse` and `MRecalculate`.
- Arbitrary pyramid construction depth in `MSuper`. MVUtensils’ `onelevel` offers only one level or the complete pyramid. Analysis can choose how many existing levels to use, but the super clip cannot be constructed with an arbitrary intermediate number of levels.
- Two pyramid reduction filters: the old shifted-triangle `rfilter=1` and quadratic `rfilter=3`. MVUtensils retains equivalents of old modes 0, 2 and 4 only. Pinterf’s five filters are documented here. :chatgpt-content-reference{index="4"}
- `MSuper(chroma=false)`, chiefly a memory/performance feature. MVUtensils can ignore chroma during analysis, but its super-clip API has no luma-only construction option.
- Multi-vector `MRecalculate(tr=…)`. MVUtensils accepts an array of independent vector clips, but it does not consume pinterf’s single interleaved `multi=true` vector stream with an explicit temporal radius.

Compensation and interpolation

- `MBlockFPS` — the block-based interpolator, including its modes 0–8. This is algorithmically distinct from `FlowFPS`, not merely an alias. MVUtensils explicitly lists `BlockFPS` as removed. :chatgpt-content-reference{index="5"}
- `MFlow(mode=1)` — forward/scatter projection from the reference frame, useful for visualizing holes and producing certain occlusion masks. MVUtensils keeps only legacy `mode=0`.
- `MFlowFPS(mask=0)` — the original, simplest and fastest occlusion-mask algorithm. MVUtensils’ `extramask` boolean represents only old `mask=1` and `mask=2`. :chatgpt-content-reference{index="6"}
- Per-pixel time maps (`tclip`) in `MFlow` and `MFlowInter`. These allow every pixel to have a different compensation/interpolation time instead of using one global `time` value.
- Several `MCompensate` modes and controls:
- `scbehavior=false`, selecting the reference rather than source frame on scene changes
- recursive compensation via `recursion`
- multi-compensation via `tr`
- insertion of a separate center clip via `center`/`cclip`
- distance-dependent rejection using `thsad2`
Pinterf’s expanded `MCompensate` interface documents these controls. :chatgpt-content-reference{index="7"}

Mask and scene-detection compatibility

- Classic `MSCDetection` mask output. The AviSynth filter produces a visual black/`Ysc` mask. MVUtensils instead passes the clip through and sets `_SceneChangePrev`/`_SceneChangeNext` properties.
- Classic multi-plane `MMask` layout. MVUtensils deliberately returns a single grayscale plane. Scripts that consume the old luma/chroma mask arrangement are not drop-in compatible. :chatgpt-content-reference{index="8"}

Degrain limits and output modes

- Temporal radius above 25. MVUtensils exposes `Degrain1` through `Degrain25`; pinterf’s `MDegrainN` permits a radius up to 128.
- High-precision output modes:
- stacked-LSB `lsb=true`
- native 16-bit output from 8-bit input via `out16=true`
- pinterf’s limited/development `out32` path on fixed-radius Degrains
MVUtensils always returns the input sample type. Pinterf’s release history confirms the native `out16` feature. :chatgpt-content-reference{index="9"}
- Planar RGB consumers. Pinterf supports planar RGB in `MSuper`, compensation, Degrain and much of the Flow family; MVUtensils accepts only GRAY and YUV. This is separate from the thread’s request for *interleaved* RGB/YUV support. :chatgpt-content-reference{index="10"}

DePan-family omissions

MVUtensils carries the four principal DePan operations, but not the complete pinterf/DePan interface:

- `DePanInterleave`
- `DePanScenes`
- Reading motion from a Deshaker/DePan `inputlog`
- Writing motion logs from `MDepan`/`DePanEstimate`
- `MDepan(range=…)` multi-frame global-motion estimation
- `DePanEstimate`’s `range`, `log`, `debug`, `extlog`, `fftw`, and `fft_threads`
- `DePanStabilize`’s `vdx`, `vdy`, `vzoom`, and `vrot` script-variable outputs
- `DePanStabilize(debuglog=…)`
- Stabilization methods `-1` and `2`; MVUtensils supports only methods 0 and 1. Even the older VapourSynth port explicitly documented methods −1 and 2 as unavailable. :chatgpt-content-reference{index="11"}

The most practically important omissions, in my view, are `MScaleVect`, `MShow`, mask kinds 3–5, `BlockFPS`, `tclip`, the missing search modes/`search_coarse`, `divide`, `scaleCSAD`, the extended `MCompensate` modes, and planar-RGB support.

Have fun. Wow this is a complicated library.

orchid
26th September 2026, 14:37
You should really verify *if* they should be restored, instead of merely copypasting some chatgpt response
Many of these filters (e.g. Finest) were removed because they objectively have no use and were legacy leftovers from poor optimization.

DTL
26th September 2026, 18:18
Many old magic and not clear hardcoded presets-like params of the MAnalyse (like temporal, trymany, pelsearch, search_coarse,global,pglobal,pzero) expected to be changed to vectors of arguments for each level control. More on this at https://github.com/pinterf/mvtools/issues/68#issuecomment-4327377980

Myrsloik
26th September 2026, 19:58
Many old magic and not clear hardcoded presets-like params of the MAnalyse (like temporal, trymany, pelsearch, search_coarse,global,pglobal,pzero) expected to be changed to vectors of arguments for each level control. More on this at https://github.com/pinterf/mvtools/issues/68#issuecomment-4327377980

I see similar things to this suggestion all the time: "allow ultra mega fine grained control of every step"

Then nobody uses it. This triggers me greatly even if I'm not the one vibe coding things here. Please provide examples of where this is used to great benefit and can't also be accomplished using recalculate.

MeteorRain
26th September 2026, 21:57
Agreed. Let's find the usage before squeezing more stuff into the project. Legacy stuff should go.

DTL
26th September 2026, 22:12
"Then nobody uses it."

The mvtools is not a final processing engine with real 100 params for MAnalyse + 100 params for MDegrainN and some immediate MVs processing filters like MAverage to form complex filtergraphs with each enduser. It is mostly building blocks for immediate complex script writers like QTGMC or SMDegrain and after that layer of abstraction most of endusers got final working processing script function with tuned presets like QTGMC(preset=slow) and all 200 params of mvtools hidden inside preset tables inside wrapper script.

Yes - some users can make very simple scripts with direct usage of MSuper+MAnalyse+ some MC filters but filter defaults may be enough and not need to set up 100 params for each filter manually every time.

Also in the 2026 we have good AI helpers tools and they can help to create finely steps preset scripts with fine performance/quality tuning balance like in 20 or 50 steps and user can call something like QTGMC(PQbalance=1) for fastest processing and QTGMC(PQbalance=50) for highest quality. Instead of about 9 named presets. We already face the situation that the 'placebo' preset in QTGMC can be expanded much more, something like from placebo1 (placebo start) to placebo10 (placebo high).

mvtools filters not only complex enough but allows to easily create performance difference in many decimal orders of magnitude from fastest to highest quality params tuning and filtergraphs complexity (see M_QTGMC script as simple example and it still not uses multi-generations MVs refining for natural noised sources using dual-input MAnalyse).

"Please provide examples of where this is used to great benefit and can't also be accomplished using recalculate."

Making MAnalyse algorithms params exposed as filter params instead of internal hacky presets and limited controls (why we can only set pnew and pglobal and pzero and can not set any other predictors penalty ? What is the magic around trymany ? Try what ? many what ? Different kinds of sources may benefit from different predictors sets and their refining or not and different penalties. With modern CPUs we can easily add many more possibly useful predictors and process for better quality and separate control of each predictor will require lots of individual new params to MAnalyse).

MRecalculate is an extra simple filter mostly for blind linear interpolation for the finer blocksize but it does not use predictors and does not use hierarchical search and can only do very small refinement around single interpolated MV. The only way to make significant quality boost with MRecalculate is to set thSAD to zero to force all new interpolated MVs refining and setup ESA search with extra large radius (up to total frame size) and this will make processing extra slow. If MV comes bad (too large error from best location) to MRecalculate it has about zero chances to fix it. Also simple linear interpolation with real world very noisy MVs fields produces lots of very average quality new MVs and quick low radius single stage refinement can not fix significant errors. Or we need to port most of the MAnalyse algorithm to MRecalculate too and use interpolated data only as one of the predictors in the next MAnalyse turn.

See the processing function of MRecalculate - https://github.com/pinterf/mvtools/blob/cdf04a5edae37b8891bb46e07593be8c3cca0868/Sources/PlaneOfBlocks.cpp#L3107 It is all it can do. No more.

Applying MRecalculate may even make MVs quality worse: Let's assume input blksize 16x16 and we request MRecalcutale interpolate and refine to blksize 8x8.
The input tessellation lattice was lucky and 2 input 16x16 blocks take motion from static part to moving part precisely at the motion/blocks border (blocks count from 1 from left to right):
MV16_1(0,0) - static part.
MV16_2(100,0) - 100 samples away moving part.

The MRecalculate makes 2x spatial interpolation for of 2 16x16 blocks into 8 8x8 blocks with linear algorithm:
MV8_1(0,0) - good predictor
MV8_2(50,0) - very bad predictor for real motion alignment to the new finer tessellation lattice
MV8_3(100,0) - good predictor

SAD check with MV8_1 and MV8_3 may be good enough and they will be passed to the output.

SAD check for interpolated MV8_2 may be very bad because true best immediate MV may be in any range from 0,0 to 100,0 depending on real motion and its alignment to the new 8x8 blocks tessellation lattice. With too bad SAD the refining attempt triggered but the typical single attempt refining search with radius of 2..3 too small to check all required range from 0,0 to 100,0 and very badly refined MV8_2 passed to the output because no other ways in the current MRecalculate algorithm present to make it better. Current MRecalculate can not save from large interpolation errors and only can refine small/slow enough movements or small input or interpolation errors. To successfully correct large interpolation errors of MV8_2 predictor with current MRecalculate user can only set thSAD to very low value and set single stage refine search radius to very large like maxMVlength/2 about 50 in this example and this will run very slow. Refining search with radius 50 will scan all range of 0 to 100 and will finally find the real best MV for MV8_2 at the edge of the interval like (100-8,0). MRecalculate does not use multi levels hierarchical search for refining like MAnalyse do so its single level large radius search will be much slower. This is +1 issue to fix in the future development of mvtools.There is also smooth=0 mode in MRecalculate with point-like MVs interpolation mode instead of linear and users can try to call MRecalculate twice with smooth=1 and smooth=0 and make external best SAD check to get better MVs from these 2 calls in current implementation (with some external SAD check tools for 2 input mvclip streams like in MAverage filter). Or new modes like dual-interpolation (or several other interpolated predictors check) +selection best SAD MV need to be added to MRecalculate like smooth=2 mode. Update: Partially fixed in that commit https://github.com/DTL2020/mvtools/commit/c6880084b5ea1b3e985b632e63425f1590588d3f .

" request for *interleaved* RGB/YUV support."

Interleaved 4:4:4 support expects to add performance to both memory service and computing of colour sampled images.

Current mvtools block SAD computing is the sum of Y SAD and U SAD and V SAD (LumaSAD() and if chroma enabled - compute U SAD and V SAD and sum). For separated planes colour data storage this means 3 (multiplied to block height with image buffer stride) different memory read streams (may sit in different SDRAM banks and pages) and mostly probably maps to different cache lines.

When it comes to computing at large datawords of AVX2 and AVX512 and typical small block sizes of 8x8 this causes extra low utilization of 32..64 byte SIMD words with only 8 byte rows computing for Y blocks and 2 separate computing of 4x4 U and V blocks for 4:2:0 formats by 4 bytes. So most of SIMD SAD operations performed with no useful data at the large 32..64 byte datawords.

With interleaved 4:4:4 formats all colour picture sample data (of 3 channels) stored in a sequential byte stream (3 bytes for 8bit and 6 bytes for 16bit samples). Full colour block row fetched from a single memory read point and may fit in a single cacheline for typical small blocks. This may increase memory throughput up to 3 times. So small block of 8x8 with interleaved RGB/YUV and 'chroma=true' is really 24x8 bytes block with 8bit and 48x8 bytes block with 16bit samples and SAD can be computed with single call to SIMD function like ColourSAD() operating with 32 (AVX2) or 64 bytes (AVX512) datawords. This causes much better SIMD computing resources utilization (3 times) and about 3x more SAD computing performance with higher quality chroma=true ME mode.

Google AI expects about +40..60% memory subsystem throughput better for interleaved YUV/RGB data storage and about 2..2.5 times better performance in SIMD SAD computing (best with AVX-512 with 2 rows colour SAD for 8x8 block per 1 cpu cycle with VDBPSADBW instruction, 2 colour rows of 24 bytes interleaved YUV/RGB fits in 1 512bit dataword and can be loaded/shuffled fast enough from 2 virtual memory locations).

For >8bit (16bit samples) interleaved 8x8 blocks google suggests AVX512 SAD computing as

__mmask32 load_mask = 0x00FFFFFF;

__m512i src_row = _mm512_maskz_loadu_epi16(load_mask, ptr_src);
__m512i ref_row = _mm512_maskz_loadu_epi16(load_mask, ptr_ref);
__m512i max_val = _mm512_max_epu16(src_row, ref_row);
__m512i min_val = _mm512_min_epu16(src_row, ref_row);

__m512i abs_diff = _mm512_sub_epi16(max_val, min_val);

sad_acc_32 = _mm512_add_epi32(sad_acc_32, _mm512_cvtepu16_epi32(_mm512_castsi512_si256(abs_diff)));

DTL
27th September 2026, 12:16
" post-2.7.45 pile (hex lattice, HW ME, extra MDegrain knobs, etc.)"

Some revised new more important features list for neo-mv (many small optimization features/changes from main list skipped as possibly included by AI in new version):

1. MDegrain(N) auto-tr (where MDegrainN with tr variable available) and auto-thSAD. PQ: Performance (with variable tr) and quality. Status: Auto-thSAD implemented (as arithmetic mean of SADs below thSCD1 with additional multiplier and offset as params.

2. SIMD multi-blocks search while possible with MVs coherence for current search area. Unroll main y/x blocks field scan loops in PseudoEPZSearch function of MAnalyse with several SIMD-parallel sub-threads with several blocks processing per single loop spin. PQ: Performance. Status: Started as design example.

3. Skip more checking of already checked predictors. Keep memory of already checked predictors MV(dx, dy) and skip checking again. PQ: Performance. Status: Implemented.

4. Make runtime sub-shifting for MC filters (function of mvtools core classes like MVPlane) for pel>1. May also be faster for ME filters at new CPUs. PQ:Performance. Status: Implemented for Wiener highest quality interpolation.

5. Make low-pass filtering of MVs at time axis to reduce more MVs noise from natural noised sources and unavoidable conversion of samples values interframe noise to MVs noise at ME operations. PQ: Quality. Status: Implemented as MVLPF with sinc- and gauss- based kernels (currently inside MDegrainN but may be external MVs processing filter as scripting element or feature of the more complex MVs processing filter).
5.1 Mostly Degrain specific: Adjust (decrease) dissimilarity metric (block weight) after MVLPF because after MVs adjustment they may no point to best SAD position and this can cause lowering degrain weight.
5.2. Add some non-linear temporal filtering of MVs (like median or others). PQ: Quality. Status: To Do.

6. Mostly hardware ME dependant: Hardware ME engines do not output dissimilarity metrics for blocks (like SAD or other possible) so it is subject to compute either at HW accelerator as some computing task (like with compute shaders at DirectX) or as task for MAnalyse to update dissimilarity metric only at the MVs stream from external ME engine. PQ:Performance/Quality. Status: Implemented as compute shader and optional MAnalyse mode.

7. Diagonal real search and interpolation of the overlap in the MVs clip. Current mvtools uses 4x H+V+diagonal max possible quality overlap mode but this causes too big performance impact. For many use cases (fight blocky artifacts at the MC filters) the simple interpolated 2x overlap is enough and saves from much slower full real 4x overlap onCPU search. Better performance/quality balance options included:
7.1. Real diagonal only (2x blocks number 2x higher performance in comparison with full 4x overlap=blksize/2 in current mvtools) search in MAnalyse and support of 2x diagonal only overlar in all MVs consuming filters.
7.2. Interpolated 4x and 2x diagonal MVs clip from 1x (non-overlapped max performance) search MVs input clip. Can be separate filter or built in function in supported/important filters.
PQ:Performance/Quality. Status: Implemented as 4x and 2x diagonal interpolation built in MDegrainN. May be more useful with hardware ME providers with no overlap MVs streams supported (or required special hacks like 2 ME calls with the diagonal shifted buffers or offsets).

8. Add more predictors to MAnalyse. Several more spatial and more temporal predictors avaialble. From simple more surround spatial to some linear/non-linear filtered spatial and temporal. PQ:Quality. Status: To Do.

9. More flexible predictors control via vectors params to MAnalyse (described above + link to github). PQ:Performance/Quality. Status: To Do.

10. New MAverage fiilter for scripting: Accepts one or several MVs input streams for the same source and perform different possible math averaging (or other post-processing even in the case of 1 input source like temporal filtering) operations on the 3-parameters MVs objects (dx, dy, dissimilarity). Can be used for complex MVs refining from different ME providers or ME functions/filters with different arguments calls etc. PQ: Quality. Status: Partially implemented with several averaging modes (about 5..8 modes).

11. Add blocks-level median (or median-like) averaging mode to Degrain filter. It is block-granularity motion-compensated degrain or spots/defects removing mode (with same selection algorithm as in vsTTempSmooth(pmode=1) but at the block-sized granularity level instead of individual sample-level. PQ:Quality. Status: Mostly implemented in MDegrainN.

12. Add optional different image dissimilarity metrics computing in addition to SAD (like SSIM or VIF or others) to all places where currently SAD only dissimilarity metric for blocks dissimilarity analysis used (in ME and where applicable MC filters). It may be additional way to process noised or other ways distorted sources with better quality. PQ:Quality. Status: SSIM and VIF and combinations of partial these metrics components implemented (for selected block sizes and bitdepths with SIMD) as bit flags. Most of image comparison metrics looks like based on 2 only math metrics of random arrays: variance and co-variance.

13. Add complex statistic collection and text debug print overlay for parameters tuning of complex MC filters (like Degrain or MCompensate or others). Like number of blocks skipped or processed by thSAD and other thresholds. PQ:Debug/Setup/Tuning. Status: To Do.

14. Add input mask clip to MC filters (Degrain, MCompensate and may be others) for additional weight and source selection tuning in comparison with internal dissimilarity metric weighting and/or thresholding. To allow script designers of additional control of filter processing (like forcing or disabling degrain of some masked frame areas or fine tuning of the degrain strength). PQ: Quality. Status: To do.

15. Add Area mode ME to some or all ME filters: Make several searches with sampling src block around current block center (offset src block coordinates to +-dx and +-dy) and compute some (same as MAverage filter do) average of the produced MVs to output final MV for this block in the output MV clip. The area size may be up to blocksize/2 to every direction from center block position. PQ:Quality. Status: Implemented as Area mode in MAnalyse.

16. Add internal multi-generations MVs refining inside Degrain filters. Sort of many iterative degrain + MAnalyse-like action inside single Degrain filter to save from large external script-based filters loops with lower performance (better spatial memory read-write locality for src and ref frames blocks inside single filter). PQ:Quality. Status: Started in MDegrainN.

17. Add second input in MAnalyse for script-based multi-generations MVs refinig with unchaged input source as one of 2 compared clips. Very simple feature: simply add second 'super' clip input to MAnalyse and if present get ref (or src) frames from this second input instead from single input super clip. PQ:Quality. Status: Implemented.

MysteryX
28th September 2026, 03:51
I just copy/pasted the GPT analysis because you guys know more what to do with it than I do.

You got the list of what's missing; now just got to figure out which ones are worth re-adding.

The Hexagonal MVs mentioned earlier, can this be done? If it can work, it probably would massively reduce the stripes artifact problem(!)

MeteorRain
28th September 2026, 04:45
Astra would like to clarify what “Hexagonal MVs” means here, since there seem to be two potentially different ideas:

- **DTL:** Supporting a hexagonal **image sampling lattice** alongside the existing Cartesian lattice. Astra's understanding is that this changes how image samples are arranged and requires corresponding changes to the matching/SAD and motion compensation kernels.
- **MysteryX:** Using “Hexagonal MVs” to reduce stripe artifacts. Is this referring to DTL's image sampling proposal, or to changing the **arrangement of motion estimation blocks / motion vectors** while keeping the image pixels on a normal rectangular grid?

Which part do you want to change: the image sampling lattice, the block/MV layout, or both? And is the proposed diagonal overlap mode a separate feature, or intended as a step toward this?

A sample showing the stripe artifacts you want to address would also help distinguish the intended goal from the implementation.

DTL
28th September 2026, 06:42
Diagonal 2x overlap is just as overlap working in BlockOverlap https://github.com/DTL2020/BlockOverlap old filter (may be old first attempts to fix blocky artifacts of the block-based filters for AVS).

Currently mvtools with overlap=blksize/2 makes 4x blocks count:
1. pos(0,0)
2. pos(0+0.5, 0)
3. pos(0, 0+0.5)
4. pos(0+0.5, 0+0.5)

The diagonal 2x overlap only adds +1 diagonally shifted block grid:
1. pos(0,0)
2. pos(0+0.5, 0+0.5)

And this is 2x times faster to process (2x less blocks to process in comparison with 4x blocks number increasing) with comparable quality of bloky artifacts removing.

DTL
28th September 2026, 18:32
The most important addition to the old mvtools we need is support for more transforms. Of both geometric and levels/lighting transforms.

From the base geometric transforms (scale/rotate/translate) the old mvtools only support translate transform. So in new generations we need to add scale and rotate transforms to be estimated and compensated.

Examples of levels/lighting transforms may be fade transforms (fade in/out effects). Its description data may be set of y=K1*x+K0 linear (polynomial ?) approximation coeffs for each colour channel. May be with 2nd order member too like y=K2*x^2+K1*x+K0 where y is transformed block data and x is source non-transformed block data and K2, K1, K0 are transform data to be estimated and applied to get backward compensated (x from y ?) data.

The rotate transform data may be a simple rotation angle for the block.

Scale transform data also need to be separated to each of 2 2D axes and may be scale_x and scale_y data.

So the new versions of transform estimation and compensation tools (ttools ? tectools ?) for each block may estimate (and compensate in some order) the block transform data structure (for 3planes/channels colour blocks):
BlockTransformData
{
Scale{scale_x, scale_y},
Rotate{rot_angle}, // single or several ?
Translate{dx, dy}, // as in old mvtools
Levels{{K2_ch0,K1_ch0,K0_ch0},{K2_ch1,K1_ch1,K0_ch1}, {K2_ch2,K1_ch2,K0_ch2},
Dissimilarity{dm} // SAD in old mvtools, the residual dissimilarity metric between src and ref blocks after all supported transforms compensated
}

An old mvclip may be renamed to transforms_data_clip. To make transform data clip compatible between different estimation and compensation tools may be good to add validity flags for each transform (to mark if data valid instead of zero data values).
So BlockTransformData structure may be structure of members like
Transform{
TransformType, TransformData(...), Flags(...)
}

In the future some more transforms types may be added like skew transform as typical geometric distortion from rolling-shutter camera sensors.

The order of the importance (from higher to lower) of still not present transforms in old mvtools expected:

1. Levels/lighting. mvtools frequently fails at the simple global fades in/out or local lighting/shading changes. At least linear transform may be added first with faster computing (K1 and K0 members). Though with typical processing of data in system transfer domain (from average non-linear gamma at old SDR systems to highly non-linear transfer function of new HDR systems) may need K2 and even higher order members for better quality.
2. Rotation.
3. Scaling.

MysteryX
29th September 2026, 06:32
I've been dealing with StripeMask detection in FrameRateConverter (might need re-calibration with updated plugins and VapourSynth port). But before dealing with it externally;

If MvTools itself can do a better job from the start, that's a plus. Like testing if something can be done with hexagonal blocks (I really don't know much about that nor how it would work).

There are torture clips here for interpolation.
https://forum.doom9.org/showthread.php?t=187118
Here, 'forInterpolation.mp4' has a lot of stripes. Comes out absolutely ugly unless I mask it out and replace with another clip.
https://drive.filejump.com/drive/s/3IjwAK2K2E2dfUPs0oX2wnpXv0cG3a

DTL
29th September 2026, 06:44
Hexagonal shaped blocks may or may not help significantly. The overlap processing in mvtools already turns the final square block to MC to something close to round shape using a soft blending mask. But the processing of hexagonal blocks with SIMD may be more complex (need masking edges of some rows and masks are different for different rows). Support of hexagonal sampling lattice may or may not include hexagonal blocks support. Support of hexagonal blocks may be nice to have feature to test too.

MysteryX
29th September 2026, 14:58
may or may not help significantly

I think it will get implemented out of curiosity; and only then we'll have the answers

DTL
29th September 2026, 23:39
Got a new idea about MAnalyse - it may have the same issue as MRecalculate with interpolation of predictors between levels. It also create each new level predictors as somehow (bilinearly ? https://github.com/pinterf/mvtools/blob/cdf04a5edae37b8891bb46e07593be8c3cca0868/Sources/PlaneOfBlocks.cpp#L597) interpolated MVs from previous level. And if source MVs from previous level are too far scattered this creates not nice predictors with too large errors in comparison with typical search radius and refining search may miss real best block position tracking via several levels of hierarchical search. The only ways to fix this interpolated predictor error is if some other predictors (like 3 surround predictors at current level) will save the search (better with trymany enabled and if predictors points not too far away from required position).

This can be tested with any mvtools (or new same algorithm implementations) with simply significant increasing of static search radius and check if it helps at the complex fast motion scenes or other test cases. If yes - this means this issue also needs some workaround like adaptive search radius mode (lower performance but better quality).

Test of 'placebo' search radius setting may be up to MAnalyse(searchparam=max(width/2, height/2)).

MeteorRain
30th September 2026, 01:52
https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/actions/runs/36646329349

New feature test build

Astra:

I've implemented an experimental Recalculate option based on your suggestion to compare interpolated predictors:

`multipredict=true`

For each target block, it evaluates both the bilinear and nearest-neighbour predictions using the selected metric, then refines the one with the lower error. If their errors are equal, it keeps the prediction selected by `smooth`.

This selects the starting predictor **before refinement**; it does not run two complete searches and compare their final results.

The option defaults to false, preserving existing behaviour and the vector format. It is available in both AviSynth and VapourSynth:

```avs
r = neo_mv_Recalculate(super, vectors, multipredict=true)
```

```python
r = core.neo_mv.Recalculate(super, vectors, multipredict=True)
```

The implementation is on `feature/recalculate-multi-predictor`. Basic correctness tests and calls through both hosts pass; practical quality and performance benefits still need user testing.

Feedback on cases where this improves or worsens the result would be welcome, ideally with a short sample and the relevant settings.

MeteorRain
30th September 2026, 04:47
https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/actions/runs/36665969926

New feature test build

Astra:

I've added a first experiment for the chroma subpixel precision issue on a separate branch, `feature/chroma-subpixel-precision`.

`Compensate` and `Degrain` now accept `chroma_subpel=true` (default: `false`). This preserves the fractional chroma displacement that would otherwise be lost when converting motion vectors for subsampled chroma.

For 4:2:0, the effective chroma sampling steps are:

- `pel=1`: 1/2 chroma pixel
- `pel=2`: 1/4 chroma pixel
- `pel=4`: 1/8 chroma pixel

For 4:2:2, refinement applies horizontally. Luma and 4:4:4 sampling remain unchanged. Existing Super clips and vectors can be reused.

AviSynth+:
```avs
compensated = neo_mv_Compensate(clip, s, v[0], chroma_subpel=true)
denoised = neo_mv_Degrain(clip, s, v, chroma_subpel=true)
```

VapourSynth:
```python
compensated = core.neo_mv.Compensate(clip, s, v[0], chroma_subpel=True)
denoised = core.neo_mv.Degrain(clip, s, v, chroma_subpel=True)
```

This version uses linear interpolation between existing Super phases. It does not yet generate the additional phases directly with Wiener interpolation, change chroma sampling during motion estimation, or cover the Flow filters.

The implementation supports 8-bit, 16-bit and float. Scalar/SIMD tests and basic execution checks in both hosts pass, but visual quality and performance on real clips still need evaluation. Enabling it adds CPU and temporary-memory overhead.

Feedback on clips where the lost chroma precision is visible would be particularly useful. This branch is independent of the earlier Recalculate multi-predictor experiment.

MeteorRain
30th September 2026, 04:59
https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/actions/runs/36666796021

New feature test build

Astra:

I've added another experiment based on DTL's point about interpolated predictors between Analyse levels:

`parentpredict=true`

When neighbouring coarse-level vectors describe different motions, their interpolated vector can fall far from either actual match. This option keeps the existing interpolated predictor and adds up to four individual parent-level vectors as additional search seeds.

The vectors are scaled to the current level and pel precision, clipped to the legal search domain, and deduplicated against existing seeds. They use the same distance penalty as spatial predictors, with the original interpolated predictor remaining the penalty anchor. Equal costs preserve the existing candidate order.

The option works with the existing `trymany` modes:

- `trymany=0`: select the lowest-cost seed, then refine it.
- `trymany=1`: refine each seed on non-finest levels.
- `trymany=2`: refine each seed on every level.

AviSynth+:
```avs
v = neo_mv_Analyse(s, delta=1, parentpredict=true)
vectors = neo_mv_AnalyseMany(s, radius=1, parentpredict=true)
```

VapourSynth:
```python
v = core.neo_mv.Analyse(s, delta=1, parentpredict=True)
vectors = core.neo_mv.AnalyseMany(s, radius=1, parentpredict=True)
```

It defaults to false and has no effect with single-level analysis. The vector format is unchanged, so downstream filters need no modifications.

In a synthetic two-level motion-boundary test, the additional parent seed recovered an exact match that the original narrow search missed. Core tests, scalar/SIMD comparisons, and basic calls through both hosts pass. Real-clip quality and performance still need testing: extra candidates cost time, particularly with `trymany=2`, and better visual results are not guaranteed.

The branch is `feature/analyse-parent-predictors`, independent of the previous Recalculate and chroma subpixel experiments. Feedback on fast motion and motion boundaries would be useful, ideally comparing the option on/off with otherwise identical settings.

MeteorRain
30th September 2026, 05:22
https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/actions/runs/36668425810

New feature test build

Astra:

Another independent experiment is ready: runtime six-tap Wiener chroma sampling in Compensate and Degrain, enabled with chroma_wiener=true (default: false).

It samples directly from the integer Super plane, retaining fractional chroma displacement down to 1/8 pixel for 4:2:0 at pel=4. Signed intermediates are retained through both axes, with rounding and clipping only at the final integer output. Float output preserves finite overshoot.

The half-pixel kernel is the existing Wiener six-tap kernel. Other phases use coefficient interpolation; the eighth-pixel coefficients are an explicit extension of that rule, not independently optimized kernels. Compared with the earlier linear experiment, the main differences are intermediate precision/clipping and edge handling. I am not claiming a general quality improvement.

This currently covers Compensate and Degrain only. Motion estimation and Flow filters are unchanged. Super still generates its usual phases, so there is no Super memory saving yet. Runtime filtering is scalar, followed by the existing composition kernels.

Tests cover pel=1/2/4, 4:2:0/4:2:2, integer and float samples, fractional phases and boundaries. Both host smoke tests passed. Practical quality and performance feedback would be useful.


Please share what above features you have tested and confirm that you want to add.

MeteorRain
30th September 2026, 05:48
https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/actions/runs/36670462729

New feature test build

Astra:

I've implemented an experimental staggered/hexagonal motion-analysis branch, with three modes for comparison:

- `layout=0`: the existing rectangular grid.
- `layout=1`: staggered rows with rectangular SAD.
- `layout=2`: the same staggered grid with hexagonal SAD, normalized by the actual sample count.

Spatial and pyramid predictors follow the staggered geometry, and FlowFPS reconstructs the motion field using triangular interpolation. Images themselves remain on the ordinary rectangular pixel lattice.

Comparing modes 1 and 2 isolates the SAD support shape. Comparing modes 0 and 2 also changes the grid, predictors, interpolation, boundary handling and occlusion mask. The experimental mask uses local motion-field compression, so it differs from the existing rectangular implementation.

Example for VapourSynth:

```python
s = core.neo_mv.Super(clip, blksize=[16], overlap=[0], pad=[32], pel=2)
v = core.neo_mv.AnalyseMany(s, radius=1, layout=2, metric="sad")
result = core.neo_mv.FlowFPS(clip, s, v, num=60000, den=1001)
```

This first version supports progressive SAD analysis feeding FlowFPS. The new analysis and interpolation paths are scalar; the existing Highway kernels remain unchanged.

DTL
30th September 2026, 06:37
"it evaluates both the bilinear and nearest-neighbour predictions using the selected metric, then refines the one with the lower error. "

This keeps more precision but still may miss the best position if the difference between MVs for interpolation (4 neighbors) is too large (about 5x or more of the search radius and block size). This also may describe why larger blocks sometimes make better results. I think in the next version change the logic of the smooth=2 mode a bit: let it clamp calculated search radius to 1..searchparam. So there will be no hardcoded upper limit and the number of arguments for MAnalyse will not be changed (old scripts compatibility).
But yes - that idea of checking 2 predictors and refine best may be also helpful for immediate performance/quality balance. So it may be 2 new modes added via smooth control:
smooth=0 - only nearest predictor
smooth=1 - only bilinear predictor
smooth=2 - both predictors checked and only best refined
smooth=3 - both predictors refined and best result for output
smooth=4 - bilinear predictor with auto-selected search param from 1 to searchparam (user set max upper limit). slowest mode, best quality.

Also again as we have in the MAnalyse search we can add more natural predictors to check or check and refine in MRecalculate:
1. Zero predictor
2. Global predictor
3. Temporal predictor

These may be also some new control values for smooth param and increase quality of recalculated search to full MAnalyse search. Though either 'trymany' control also may be good for performance/quality balance or more smooth control combinations via single integer like:

smooth=5 - bilinear predictor with auto-selected search param from 1 to searchparam (bilinear checked and refined ) and Zero + Global predictors checked (if checked is better - refine around best again)
smooth=6 - bilinear predictor with auto-selected search param from 1 to searchparam (bilinear checked and refined ) and Zero + Global predictors checked and refined
smooth=7 - bilinear predictor with auto-selected search param from 1 to searchparam (bilinear checked and refined ) and Zero + Global + Temporal predictors checked (if checked is better - refine around best again)
smooth=8 - bilinear predictor with auto-selected search param from 1 to searchparam (bilinear checked and refined ) and Zero + Global + Temporal predictors checked and refined

Temporal predictor typically require to switch MT mode to about 1 threaded in AVS+ threading so costly and need separate control group.

To test new features some production working script required like QTGMC. Using new engine but also all functions new names to have ability to load both old and new QTGMC function to single script to compare. I will try to do this but it may take some time.

DTL
30th September 2026, 12:28
"runtime six-tap Wiener chroma sampling in Compensate and Degrain, enabled with chroma_wiener=true (default: false)."

For full performance benefit it need to fetch full block runtime shifted if required. Not only chroma but luma plane too. To completely stop usage of the precomputed phases from memory at MC filters.

Myrsloik
30th September 2026, 14:21
I'm curious. Do you have any sample images/clips showing quality improvements with the hexagonal search and/or better resolution chroma interpolation?

DTL
30th September 2026, 16:32
"experimental staggered/hexagonal motion-analysis branch"

To test with denoise or deinterlace production need support in Degrain and/or Compensate filters. Currently documentation says only FlowFPS support the analysed MVs stream - https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/blob/feature/hexagonal-motion/docs/experimental-hexagonal-motion.md

DTL
30th September 2026, 20:55
I'm curious. Do you have any sample images/clips showing quality improvements with the hexagonal search

Google Gemini AI promises some. An example: the smallest round-shaped enough isotropy block in the Cartesian lattice is 2x2 = 4 samples. In the triangle/hex lattice it is a triangle of 3 samples - expect support for finer image structures.The Cartesian lattice provides only 2 directions in 2D space with 1.0 distance between samples and the hex/triangle lattice provides 3 directions in 2D space with the same 1.0 intersample distance (with best spatial sampling efficiency). It is more (up to ideal ?) 2D isotropy - less distorted angled fine objects (like 30 or 60 degrees to H or V directions).
But to see we need working tools. Part is started but for best results we need a process in true hex/triangle sampling lattice.

The block shapes/sizes for hex (triangle ?) world:

The minimal round-shaping symmetry possible in the triangle sampling lattice is 1 Triangle. All other smooth round shaped hexagons may be counted in the number of Triangles (T).
Analytic equations:
Number of elementary triangles in a hexagon shaped block: 6x4^(N), where N is 0,1,2... - integers.
Horizontal size of the block in sample count (if we orient one side of the lattice triangle in horizontal direction - by shifting even rows (?)):
H=2^(N+1) + 1.
So the main sizes of the blocks to live with in hex/triangle sampling lattice word is:

1T (1 triangle). H size = 2 (samples), smallest possible and less stable to noise. May be the end of the Recalculate refining stages for finest transform support.

6T (6 triangles, elementary hexagon). H size = 3. Possibly general use of fine blocks (like 4x4 in Cartesian lattice ?).

24T (24 triangles). H size = 5. Something a bit smaller than the 8x8 Cartesian ?

96T. H size = 9. A bit larger than the 8x8 Cartesian or very close ?

384T. H size = 17. Close to 16x16 Cartesian ?

1536T. H size = 33. Close to 32x32 Cartesian ?

6144T. H size = 65. Possibly largest required (rarely).

So for Analyse it is subject to design how to enter blocksize param - as number of Triangles ? As H-size (for round-shaped smooth hexagon) ?

As Google AI says there is possible intermediate fully covering sizes for hex/triangle grid with good round-symmetry but with teethed shape. May be tested in some future.

About testing with hex sampling lattice: I started to make a mod of JincResizeMT into HexSincResizeMT to be conversion tool between Cartesian and Hexagon sampling domains - https://github.com/DTL2020/JincResizeMT . But using free Google Gemini AI we are a bit lost with coordinate recomputing between different lattices. Need some help if possible (or full redesign of filter).

Its main arguments may left the same as JincResize(MT) and main additions are input and output sampling lattices new arguments:
[il]i[ol]i -
il - input lattice
ol - output lattice
of the enumeration
typedef enum _LATTICE_TYPE
{
LATTICE_CARTESIAN,
LATTICE_HEXAGONAL,
} LATTICE_TYPE;

The current hard to debug is simple coordinate calculation function of https://github.com/DTL2020/JincResizeMT/blob/0dadcd8a6b22e30fbdd93e6df5a0c770b5d326f9/Src/JincResizeMT.cpp#L650 (generation of weights for 2D resampling engine). You may see last about 2..3 commits as attempts to fix by AI but still not good.

The usage of the filter to test any filter supporting processing in the hex sampling lattice expected (for UHD source in Cartesian lattice):

HexSincResize(1920,1080, il=0, ol=1)
#(or better with anti-Gibbs weighting UserDefined4HexResize(1920,1080, il=0, ol=1, k0=...)).
Really compress to a bit V-stretched hex space but to make frame size some standard compatible

to convert higher resolution source into hex sampling grid (about 13% more dense in Shannon limit in 2D space in comparison with Cartesian).
Next is process with filters supporting hex sampling lattice and after the end - decompress back to high resolution Cartiesian lattice with

HexSincResize(3840, 2160, il=1, ol=0)

To feed to standard Cartesian display for image evaluation.

ChaosKing
30th September 2026, 21:27
Google Gemini AI promises some.

Months ago, just for fun, I ran several tests with MVTools and asked some AIs about optimizations etc. They promised me a lot, but after implementing the suggestions and some benchmarks, the results were disappointing. And please, Gemini is such a dumb hallucinating model. Compared to the other models, it's about a year behind. Its benchmark scores look reasonably good, but on real world problems it produces high quality retard-slop. A strongly advice against it for any complex coding task.

Image gen and design is not bad with gemini.

EDIT
google just heard me, maybe v4 will be finally good: https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-4-argon/

DTL
1st October 2026, 15:03
Started to test with some production QTGMC - https://github.com/DTL2020/QTGMC/blob/main/QTGMCneo_3392n2.avsi . Only preset "slower" replaced to work with neo-mv functions now. Other are need to be replaced manually or with AI help from such template.

Good news - it is working. Not very good - the motion areas quality not as good as with mvtools2. Need to check why. May be I not correctly transfer all arguments or left some defaults in not best state.

Test script is

# Input plugins
LoadPlugin("ffms2.dll")
LoadPlugin("mvtools2.dll")
LoadPlugin("neo-mv.dll")

LoadPlugin("masktools2.dll")
LoadPlugin("plugins_JPSDR.dll")
LoadPlugin("RgTools.dll")
LoadPlugin("TDeint.dll")
LoadPlugin("AddGrainC.dll")

Import("Zs_RF_Shared.avsi")

Import("QTGMCneo_3392n2.avsi")
Import("QTGMC_3392n2.avsi")

FFmpegSource2("aos_xtr2.avi")

AssumeBFF()

neo=QTGMCneo(preset="slower").Subtitle("neo slower")
std=QTGMC(preset="slower").Subtitle("std slower")
std_smooth_r50=QTGMC(preset="slower", TR1=2, BlockSize=16, SubPel=2, Overlap=8, ChromaMotion=true, GlobalMotion=true, Refinemotion=true,\
RefineMotionAll=true, RefineMotionSearch=5, RefineMotionRadius=50, search=4,searchparam=2, trymany=true, PNew=5).Subtitle("smooth=1 rfmr=50")

Interleave(neo, std, std_smooth_r50, Subtract(neo, std))

Prefetch(6)


Test file is https://drive.google.com/file/d/1ErwplxJCtprZaEq6oq033d9GJ0E9BST7/view?usp=sharing .

Also it is confirmed: To test new features with QTGMC (at least preset "slower") required filters are:
neo_mv_Super
neo_mv_Analyse
neo_mv_Compensate
neo_mv_Degrain1

MeteorRain
1st October 2026, 21:24
Neo-mv was following MVU not the standard mvtools. I need to check the API difference between MVU and standard mvtools. You might have better luck checking the difference against jetpack one.

DTL
2nd October 2026, 06:51
Some idea about possible more blur (mostly looking like V-blur) in QTGMC with mv-neo:

mvtools uses internal fieldshift compensation without external arguments to filters (may be auto gathered from clip/frame properties flags https://github.com/DTL2020/mvtools/blob/9eedb9d0850f638fc43212fb515cf048f9b9a58f/Sources/MVAnalyse.cpp#L851) - like https://github.com/DTL2020/mvtools/blob/9eedb9d0850f638fc43212fb515cf048f9b9a58f/Sources/PlaneOfBlocks.cpp#L308 . So QTGMC does not provide special arguments to mvtools filters for fieldshift information. If mv-neo does not have this feature (or have other ways of control) it needs to be either added somehow or required controls need to be provided for QTGMC. Or the QTGMC algorithm needs to be changed to adapt neo-mv (by 'external field shift' processing ?).

Maybe it is 'fields' and 'tff' params for neo_mv_Analyse and neo_mv_Compensate ?

After addition of these params passing as QTGMCneo params -

QTGMCneo(preset="slower", AnalyseFields=true, AnalyseTFF=false)

quality looks to become even lower (more worse after addition fields and tff to neo_mv_Compensate) . Also I do not understand why neo_mv_Compensate uses fields and tff params but neo_mv_Degrain1 does not. Because DegrainX is sort of a combination of several Compensate+blending engines.

orchid
2nd October 2026, 10:04
QTGMC creates a fully progressive clip for use with mvtools operations. Any interlaced handling is irrelevant there.

DTL
2nd October 2026, 11:21
I made only quick tests but addition fields=true to Analyse (in QTGMC) makes output quality a bit better. As I remember QTGMC process input interlaced source with Bob() initial deinterlace (?) and it may left some interlaced fields data ?

orchid
2nd October 2026, 21:41
"it may left some interlaced fields data"
No chance. QTGMC temporally smooths and completely blurs out the search clip. No combing survives after that.

Emulgator
2nd October 2026, 22:26
I thought so too, but: There came Topaz, to me in late 2021.
Applying such AI uprezzing after QTGMC: Depending on QTGMC settings and previous resizing/resampling residues Topaz still might find residues.
As these days the end product often got to be 4K50p or 4K59,94p the choice of interlaced sources brings quite differing results after AI.
Appropriate filtering before incurring more damage is key.
(If DTL hints something it is worth considering anyway, we both have a background in digital signal analysis, me thinks)
MFDIN-L was where I left off end 2024. Lets go and see Selur...

orchid
2nd October 2026, 23:21
I thought so too, but: There came Topaz, to me in late 2021.
Applying such AI uprezzing after QTGMC: Depending on QTGMC settings and previous resizing/resampling residues Topaz still might find residues.
As these days the end product often got to be 4K50p or 4K59,94p the choice of interlaced sources brings quite differing results after AI.


We're talking about the search clip, not the final output. The final output can indeed have combing depending on the settings (lossless enabled), but the search clip will not.

MeteorRain
2nd October 2026, 23:27
Astra flagged some parameter difference:

--- a/QTGMCneo_3392n2.avsi
+++ b/QTGMCneo_3392n2.avsi
@@ -264,7 +264,7 @@
RefineMotionRadius = default (RefineMotionRadius, SearchParam)
MEus = default( MEus, false)
RC_smooth = default( RC_smooth, 1)
- AnalyseFields = default( AnalyseFields, true)
+ AnalyseFields = default( AnalyseFields, false)
AnalyseTFF = default( AnalyseTFF, true)


@@ -625,8 +625,17 @@
# for SubPel = 4 are we need 0.125, 0.125 or other ?
SubPel_us = (MEus) ? SubPel : 1

+ NeoSearch = 2 # pinterf search=4 (hexagon); neo search=4 is horizontal.
+ NeoLambda = Int(Round(Lambda * 64.0 / (BlockSize * BlockSize)))
+ NeoThSCD2 = ThSCD2 * 100.0 / 256.0
+
srchSuper_us = IsClip(srchSuper_us) ? srchSuper_us : \
- (maxTR > 0) ? srchClip_us.neo_mv_Super( pel=(MEus) ? 1 : SubPel, sharp=SubPelInterp, pad=[hpad*SubPel_us, vpad*SubPel_us], blksize=BlockSize*SubPel_us, overlap=Overlap*SubPel_us) : NOP()
+ (maxTR > 0) ? srchClip_us.neo_mv_Super( pel=(MEus) ? 1 : SubPel, sharp=SubPelInterp, rfilter=1, pad=[hpad*SubPel_us, vpad*SubPel_us], blksize=BlockSize*SubPel_us, overlap=Overlap*SubPel_us) : NOP()

bVec6c = IsClip(bVec6)
bVec6 = bVec6c ? bVec6 : \
@@ -686,9 +695,9 @@

bVec2c = IsClip(bVec2)
bVec2 = bVec2c ? bVec2 : \
- (maxTR > 1) ? srchSuper_us.neo_mv_Analyse(delta=-2, overlap=Overlap*SubPel_us, search=Search, searchparam=SearchParam, \
- pelsearch=PelSearch, mvlambda=Lambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \
- chroma=ChromaMotion, fields=AnalyseFields, tff=AnalyseTFF) : NOP()
+ (maxTR > 1) ? srchSuper_us.neo_mv_Analyse(delta=2, overlap=Overlap*SubPel_us, search=NeoSearch, searchparam=SearchParam, \
+ pelsearch=PelSearch, mvlambda=NeoLambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \
+ globalmv=GlobalMotion, metric="sad", chroma=ChromaMotion, fields=AnalyseFields, tff=AnalyseTFF) : NOP()

bVec2 = bVec2c || !RefineMotion || !(maxTR > 1) ? bVec2 : srchSuper_us.MRecalculate(bVec2, overlap=halfoverlap*SubPel_us, blksize=halfblksize*SubPel_us, thSAD=halfthSAD, DCT=DCT, \
chroma=ChromaMotion, truemotion=TrueMotion, search=RefineMotionSearch, \
@@ -701,9 +710,9 @@

bVec1c = IsClip(bVec1)
bVec1 = bVec1c ? bVec1 : \
- (maxTR > 0) ? srchSuper_us.neo_mv_Analyse(delta=-1, overlap=Overlap*SubPel_us, search=Search, searchparam=SearchParam, \
- pelsearch=PelSearch, mvlambda=Lambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \
- chroma=ChromaMotion, fields=AnalyseFields, tff=AnalyseTFF) : NOP()
+ (maxTR > 0) ? srchSuper_us.neo_mv_Analyse(delta=1, overlap=Overlap*SubPel_us, search=NeoSearch, searchparam=SearchParam, \
+ pelsearch=PelSearch, mvlambda=NeoLambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \
+ globalmv=GlobalMotion, metric="sad", chroma=ChromaMotion, fields=AnalyseFields, tff=AnalyseTFF) : NOP()

bVec1 = bVec1c || !RefineMotion || !(maxTR > 0) ? bVec1 : srchSuper_us.MRecalculate(bVec1, overlap=halfoverlap*SubPel_us, blksize=halfblksize*SubPel_us, thSAD=halfthSAD, DCT=DCT, \
chroma=ChromaMotion, truemotion=TrueMotion, search=RefineMotionSearch, \
@@ -716,9 +725,9 @@

fVec1c = IsClip(fVec1)
fVec1 = fVec1c ? fVec1 : \
- (maxTR > 0) ? srchSuper_us.neo_mv_Analyse(delta=1, overlap=Overlap*SubPel_us, search=Search, searchparam=SearchParam, \
- pelsearch=PelSearch, mvlambda=Lambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \
- chroma=ChromaMotion, fields=AnalyseFields, tff=AnalyseTFF) : NOP()
+ (maxTR > 0) ? srchSuper_us.neo_mv_Analyse(delta=-1, overlap=Overlap*SubPel_us, search=NeoSearch, searchparam=SearchParam, \
+ pelsearch=PelSearch, mvlambda=NeoLambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \
+ globalmv=GlobalMotion, metric="sad", chroma=ChromaMotion, fields=AnalyseFields, tff=AnalyseTFF) : NOP()

fVec1 = fVec1c || !RefineMotion || !(maxTR > 0) ? fVec1 : srchSuper_us.MRecalculate(fVec1, overlap=halfoverlap*SubPel_us, blksize=halfblksize*SubPel_us, thSAD=halfthSAD, DCT=DCT, \
chroma=ChromaMotion, truemotion=TrueMotion, search=RefineMotionSearch, \
@@ -730,9 +739,9 @@

fVec2c = IsClip(fVec2)
fVec2 = fVec2c ? fVec2 : \
- (maxTR > 1) ? srchSuper_us.neo_mv_Analyse(delta=2, overlap=Overlap*SubPel_us, search=Search, searchparam=SearchParam, \
- pelsearch=PelSearch, mvlambda=Lambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \
- chroma=ChromaMotion, fields=AnalyseFields, tff=AnalyseTFF) : NOP()
+ (maxTR > 1) ? srchSuper_us.neo_mv_Analyse(delta=-2, overlap=Overlap*SubPel_us, search=NeoSearch, searchparam=SearchParam, \
+ pelsearch=PelSearch, mvlambda=NeoLambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \
+ globalmv=GlobalMotion, metric="sad", chroma=ChromaMotion, fields=AnalyseFields, tff=AnalyseTFF) : NOP()

fVec2 = fVec2c || !RefineMotion || !(maxTR > 1) ? fVec2 : srchSuper_us.MRecalculate(fVec2, overlap=halfoverlap*SubPel_us, blksize=halfblksize*SubPel_us, thSAD=halfthSAD, DCT=DCT, \
chroma=ChromaMotion, truemotion=TrueMotion, search=RefineMotionSearch, \
@@ -912,7 +921,7 @@
# >>>> Switch to planar YUY2 during next step - remains planar until very end of script except blurring for back blending & SVThin

# InputType=2,3: use motion mask to blend luma between original clip & reweaved clip based on ProgSADMask setting. Use chroma from original clip in any case
- inputTypeBlend = (ProgSADMask > 0.0) ? MMask( srchClip, bVec1, kind=1, ml=ProgSADMask ) : NOP()
+ inputTypeBlend = ((InputType == 2 || InputType == 3) && ProgSADMask > 0.0) ? MMask( srchClip, bVec1, kind=1, ml=ProgSADMask ) : NOP()
edi = (InputType != 2 && InputType != 3) ? (!yuy2 ? edi1 : edi1.nonyuy2clipin(true)) :\
(ProgSADMask <= 0.0) ? (!yuy2 ? edi1.MergeChroma( innerClip ) : edi1.MergeChroma( innerClip ).nonyuy2clipin(true)) : \
(!yuy2 ? mt_merge( innerClip, edi1, inputTypeBlend, U=2,V=2 ) : \
@@ -920,8 +929,8 @@

# Get the max/min value for each pixel over neighboring motion-compensated frames - used for temporal sharpness limiting
ediSuper = (TR1 > 0 || temporalSL) ? edi.neo_mv_Super( pel=(MEus) ? 1 : SubPel, sharp=SubPelInterp, pad=[hpad*SubPel_us, vpad*SubPel_us], blksize=BlockSize*SubPel_us, overlap=Overlap*SubPel_us, onelevel=true) : NOP()
- bComp1 = (temporalSL) ? edi.neo_mv_Compensate(ediSuper, bVec1, thSCD1=ThSCD1,thSCD2=ThSCD2, fields=AnalyseFields, tff=AnalyseTFF) : NOP()
- fComp1 = (temporalSL) ? edi.neo_mv_Compensate(ediSuper, fVec1, thSCD1=ThSCD1,thSCD2=ThSCD2, fields=AnalyseFields, tff=AnalyseTFF) : NOP()
+ bComp1 = (temporalSL) ? edi.neo_mv_Compensate(ediSuper, bVec1, thSCD1=ThSCD1,thSCD2=NeoThSCD2, fields=AnalyseFields, tff=AnalyseTFF) : NOP()
+ fComp1 = (temporalSL) ? edi.neo_mv_Compensate(ediSuper, fVec1, thSCD1=ThSCD1,thSCD2=NeoThSCD2, fields=AnalyseFields, tff=AnalyseTFF) : NOP()
tMax = (temporalSL) ? edi.mt_logic( fComp1, "max", U=3,V=3 ).mt_logic( bComp1, "max", U=3,V=3 ) : NOP()
tMin = (temporalSL) ? edi.mt_logic( fComp1, "min", U=3,V=3 ).mt_logic( bComp1, "min", U=3,V=3 ) : NOP()
bComp3 = (SLRad > 1 && temporalSL) ? defined(bomt) ? edi.MCompensate( ediSuper, bVec3, thSCD1=ThSCD1,thSCD2=ThSCD2, mt=bomt ) : edi.MCompensate( ediSuper, bVec3, thSCD1=ThSCD1,thSCD2=ThSCD2 ) : NOP()
@@ -940,9 +949,9 @@
# MDegrain1 (motion compensated) rather than TemporalSmooth makes the weightings *look* different, but they evaluate to the same values
# Create linear weightings of neighbors first -2 -1 0 1 2
# degrain1 = (TR1 > 0) ? eval("edi.MDegrain1( ediSuper, bVec1,fVec1, thSAD=ThSAD1, thSCD1=ThSCD1,thSCD2=ThSCD2" + sMDegnl + ")") : NOP() # 0.00 0.33 0.33 0.33 0.00
- degrain1 = (TR1 > 0) ? edi.neo_mv_Degrain1( ediSuper, , thSAD=[ThSAD1,ThSAD1], thSCD1=ThSCD1,thSCD2=ThSCD2) : NOP() # 0.00 0.33 0.33 0.33 0.00
+ degrain1 = (TR1 > 0) ? edi.neo_mv_Degrain1( ediSuper, [bVec1, fVec1], thSAD=[ThSAD1,ThSAD1], thSCD1=ThSCD1,thSCD2=NeoThSCD2) : NOP() # 0.00 0.33 0.33 0.33 0.00
# degrain2 = (TR1 > 1) ? eval("edi.MDegrain1( ediSuper, bVec2,fVec2, thSAD=ThSAD1, thSCD1=ThSCD1,thSCD2=ThSCD2" + sMDegnl + ")") : NOP() # 0.33 0.00 0.33 0.00 0.33
- degrain2 = (TR1 > 1) ? edi.neo_mv_Degrain1( ediSuper, [bVec2, fVec2], thSAD=[ThSAD1,ThSAD1], thSCD1=ThSCD1,thSCD2=ThSCD2) : NOP() # 0.33 0.00 0.33 0.00 0.33
+ degrain2 = (TR1 > 1) ? edi.neo_mv_Degrain1( ediSuper, [bVec2, fVec2], thSAD=[ThSAD1,ThSAD1], thSCD1=ThSCD1,thSCD2=NeoThSCD2) : NOP() # 0.33 0.00 0.33 0.00 0.33
degrain1 = (TR1 > 0) ? lsb ? degrain1.ditherpost(mode=6, slice=false) : n16 ? degrain1.ConvertTo8bit(dither=1) : degrain1 : degrain1
degrain2 = (TR1 > 1) ? lsb ? degrain2.ditherpost(mode=6, slice=false) : n16 ? degrain2.ConvertTo8bit(dither=1) : degrain2 : degrain2

@@ -1009,7 +1018,7 @@
# Final light linear temporal smooth for denoising
stableSuper = (TR2 > 0) ? addNoise1.neo_mv_Super( pel=(MEus) ? 1 : SubPel, sharp=SubPelInterp, pad=[hpad*SubPel_us, vpad*SubPel_us], blksize=BlockSize*SubPel_us, overlap=Overlap*SubPel_us, onelevel=true) : NOP()
stable = (TR2 == 0) ? addNoise1 : \
- (TR2 == 1) ? eval("addNoise1.neo_mv_Degrain1( stableSuper, [bVec1,fVec1], thSAD=[ThSAD2,ThSAD2], thSCD1=ThSCD1,thSCD2=ThSCD2" + sMDegnl + ")") : \
+ (TR2 == 1) ? addNoise1.neo_mv_Degrain1( stableSuper, [bVec1,fVec1], thSAD=[ThSAD2,ThSAD2], thSCD1=ThSCD1, thSCD2=NeoThSCD2) : \
(TR2 == 2) ? eval("addNoise1.MDegrain2( stableSuper, bVec1,fVec1, bVec2,fVec2, thSAD=ThSAD2, thSCD1=ThSCD1,thSCD2=ThSCD2" + sMDegnl + ")") : \
(TR2 == 3) ? eval("addNoise1.MDegrain3( stableSuper, bVec1,fVec1, bVec2,fVec2, bVec3,fVec3, thSAD=ThSAD2, thSCD1=ThSCD1,thSCD2=ThSCD2" + sMDegnl + ")") : \
(TR2 == 4) ? eval("addNoise1.MDegrain4( stableSuper, bVec1,fVec1, bVec2,fVec2, bVec3,fVec3, bVec4,fVec4, thSAD=ThSAD2, thSCD1=ThSCD1,thSCD2=ThSCD2" + sMDegnl + ")") : \



This patch aligns the basic QTGMCneo [b]Slower path with the intended pinterf MVTools settings. Several parameters have different meanings between the two APIs:

- **Search:** pinterf’s `search=4` selects hexagon search; neo-mv uses `search=2`. Passing `4` unchanged selects horizontal search.
- **Lambda:** QTGMC already scales `Lambda` by block area, whereas neo-mv applies that scaling internally. The patch converts it back using `Lambda * 64 / (block_width * block_height)`. For the default 16×16 blocks, `400` becomes `100`.
- **Scene detection:** pinterf interprets `ThSCD2` on a 256-based scale; neo-mv expects a percentage. QTGMC’s `98` therefore becomes `38.28125`, rather than 98%.
- **Fields:** the motion-search clip is already bobbed into full-height progressive frames. Field adjustment is disabled in both analysis and compensation. The original source’s BFF/TFF setting still matters for deinterlacing.
- **Vector direction:** pinterf’s `isb=true` references `n+delta`. The patch restores positive deltas for `bVec` and negative deltas for `fVec`. Swapping a symmetric Degrain pair alone is not evidence of a quality problem.
- **Other controls:** `GlobalMotion` is forwarded explicitly, and SAD is selected explicitly. `rfilter=1` makes neo-mv’s existing default explicit; it corresponds to pinterf’s default four-tap reduction filter, numbered `2`.

The patch also avoids creating an unused legacy `MMask` for interlaced input and removes the legacy `mt` argument from the final neo-mv Degrain call.

This is a limited migration baseline. Assertions exclude branches that still use legacy MVTools calls, including motion refinement, source matching, noise processing, and larger temporal radii.

The patch applies cleanly and passes a single-frame test through the complete Slower pipeline with a small blank source. Full-sample initialization has not completed locally, so this does **not yet establish that the reported quality difference is resolved**, or that the two implementations produce identical results.

orchid
3rd October 2026, 01:38
Neo-mv was following MVU not the standard mvtools. I need to check the API difference between MVU and standard mvtools. You might have better luck checking the difference against jetpack one.

vs-jetpack is already ported to MVU, so assuming neo-mv api = mvu api then its simply a find+replace change. Avisynth scripts using old mvtools will need quite a bit of adjusment.
I personally suggested some of these api changes as the old mvtools api had major flaws.

MeteorRain
3rd October 2026, 06:21
vs-jetpack is already ported to MVU, so assuming neo-mv api = mvu api then its simply a find+replace change. Avisynth scripts using old mvtools will need quite a bit of adjusment.
I personally suggested some of these api changes as the old mvtools api had major flaws.

I already planned to convert vs-jetpack into Ruby, with the eco system I'm creating. But filling the gaps will take some time. (Scene change detection, etc., needs to be ported as well.)

DTL
3rd October 2026, 10:46
Astra flagged some parameter difference:


Yes - that mostly fixed differences and testing may continue. But fixing the search param to 2 internally is not nice for usage of different presets and manual input.
I change 4 to 2 in the presets table but what is the close or full replacement in the neo-mv of the original mvtools search 5 (Uneven Multi Hexagon (UMH) search, searchparam is the range. (similar to x264).) and 0 ('OneTimeSearch'. searchparam is the step between each vectors tried (if searchparam is superior to 1, step will be progressively refined). ?

Also tried to enable RefineMotion processing with neo_mv_Recalculate in the version https://github.com/DTL2020/QTGMC/blob/0e88f2ac76b3c028a1468d88ead7f2d3fc7af5af/QTGMCneo_3392n2.avsi . But the system reports an error 'invalid arguments to neo_mv_Compensate at line 927' if the function is called as QTGMCneo(preset="slower", RefineMotion=true) . If Refinemotion=false it is working. Looks like bVec1 clip becomes somehow damaged if RefineMotion enabled (after neo_mv_Recalculate) but I do not know how and how to fix it.

As documentation says all params for neo_mv_Recalculate are independent from blocksize so we no longer need to scale Lambda value for lower sized blocks with refining ?

DTL
3rd October 2026, 13:58
https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/actions/runs/36665969926

New feature test build

Astra: chroma subpixel precision issue on a separate branch, `feature/chroma-subpixel-precision`.

Test version (because of the different binary it required separate version) of QTGMC: https://github.com/DTL2020/QTGMC/blob/650e36d3689b65b479a1c41238ea8eee632a5905/QTGMCneo_csp_3392n2.avsi

Test script is for 4:2:0 YV12 DV clip

neo_std=QTGMCneo(preset="slower", Chroma_RTSP=false, SubPel=4).Subtitle("neo slower csp=f")
neo_csp=QTGMCneo(preset="slower", Chroma_RTSP=true, SubPel=4).Subtitle("neo slower csp=t")
Interleave(neo_std, neo_csp, Subtract(neo_std, neo_csp).Levels(127-5, 1, 127+5, 0, 255))


The difference in chroma output is visible with any SubPel from 1 to 4 (why ? it looks with any subpel the chroma blocks fetching is not nice in standard version ?). Though initially expected to be some difference only at the subpel=4 (because standard builds do not support 1/8 precision for 4:2:0 chroma).
The amplitude of the difference becomes lower with increasing subpel precision from 1 to 4 but still significantly above +-1LSB at 8bit and subpel=4.

With subpel=1 enabling Chroma_RTSP visibly enhances luma/chroma alignment at the fine detailed colour areas and makes coloured contrast better. With subpel=2 visual difference is lower. At the higher subpel expected also but with less difference. This is a useful feature and needs to be added to the main branch. For better ME it need to be added to Analyse (and Recalculate) too.

The performance impact is significant:
x264 crf=18 encoding at i5-9600K CPU:

pel=2
Chroma_RTSP=false: 45 fps , average bitrate 7149K
Chroma_RTSP=true: 32 fps , average bitrate 7143K

pel=4
Chroma_RTSP=false: 35 fps , average bitrate 6911K
Chroma_RTSP=true: 26 fps , average bitrate 6907K

DTL
3rd October 2026, 15:04
https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/actions/runs/36668425810

New feature test build

Astra: runtime six-tap Wiener chroma sampling in Compensate and Degrain, enabled with chroma_wiener=true


Test version of QTGMC is https://github.com/DTL2020/QTGMC/blob/main/QTGMCneo_cwrtsp_3392n2.avsi

Test script:

neo_std=QTGMCneo(preset="slower", Chroma_WRTSP=false, SubPel=2).Subtitle("neo slower wrtsp=false")
neo_csp=QTGMCneo(preset="slower", Chroma_WRTSP=true, SubPel=2).Subtitle("neo slower wrtsp=true")


The colour difference also present and comparable to the feature 'chroma-subpixel-precision'. The x264 encoding performance is better for both subpel=2 and subpel=4. If this is higher quality interpolation replacement of the feature 'chroma-subpixel-precision' it may be better to add instead.

Encoding performance at i5-9600K CPU with feature enabled:

subpel=2 33.8 fps (close to 34).
subpel=4 28 fps.

As expected runtime shifting inside CPU core makes better performance instead of precomputing and fetching from host memory even with higher taps interpolation filter ? The full runtime shifting (luma blocks too) need to be tested too.

Addition: Google AI expects enabling full runtime blocks shifting (UV and Y planes for 4:2:0) may or may not be faster depending on the CPU compute performance and memory performance. So it may be better to make many values param like runtime_shift (or runtime_interpolate) with values like:
0 - read precomputed positions from super clip (and compute them for pel >1 in Super)
1 - runtime computing only UV planes blocks (3 times less compute load for 4:2:0 formats but still memory load for Y)
2 - runtime computing for all planes (3 times more compute load for CPU core but lowest memory load also no Super clip pel >1 computing and memory store/load at some use cases where for MC separated super clip used. it is also QTGMC use case ? where srch_super is prefiltered and not usable for production MC and separate super clip is generated for MC operations this causes doubling of the phases computing and doubling of the memory store/use operations).

Also needed more params for Super to switch precomputing of shifted phases on/off (for UV or for all planes).

DTL
3rd October 2026, 16:22
https://github.com/HomeOfAviSynthPlusEvolution/neo-mv/actions/runs/36666796021

New feature test build

Astra:`parentpredict=true`

Test version of QTGMC is https://github.com/DTL2020/QTGMC/blob/7b27d142767d64dca8ec833cdb0ba5a8b8bcc340/QTGMCneo_mepp_3392n2.avsi

Test script is

neo_std=QTGMCneo(preset="slower", trymany=0, ME_PP=false, SubPel=2).Subtitle("neo slower tm=0 ME_PP=false")
neo_tm1=QTGMCneo(preset="slower", trymany=1, ME_PP=false, SubPel=2).Subtitle("neo slower tm=1 ME_PP=false")
neo_tm2mepp=QTGMCneo(preset="slower", trymany=2, ME_PP=true, SubPel=2).Subtitle("neo slower tm=2 me_pp=true")
neo_tm2mepp_sp50=QTGMCneo(preset="slower", trymany=2, ME_PP=true, searchparam=50,SubPel=2).Subtitle("neo slower tm=2 sp50 me_pp=true")
std=QTGMC(preset="slower", SubPel=2).Subtitle("std slower")

Interleave(neo_std, neo_tm1, neo_tm2mepp, neo_tm2mepp_sp50, std, Subtract(neo_std, neo_tm2mepp).Levels(127-10, 1, 127+10, 0, 255))


Switching from trymany=0 to trymany=1 makes small changes in MC quality. Enabling both trymany=2 and ME_PP feature makes bigger changes. Testing of longer MVs search with searchparam of 50 makes small enough additions to trymany=2 and ME_PP enabled (though adds high performance impact). This is a good features to add to the main build.

x264 crf=18 encoding performance impact is not small:
QTGMCneo(preset="slower", trymany=2, ME_PP=true, SubPel=2) - 30 fps (vs 45fps) and 7124K output bitrate
QTGMCneo(preset="slower", trymany=2, ME_PP=true, SubPel=4) - 25 fps (vs 32 fps) and 6868K output bitrate

Switching blocksize to 8 for DV SD makes quality even better
QTGMCneo(preset="slower", BlockSize=8, trymany=2, ME_PP=true, SubPel=4) . With 6868K output bitrare but fps 9. Waiting for Recalcuate to test blocksize refining from 16 to 8.