visqol-python 3.4.0__tar.gz → 3.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- visqol_python-3.6.0/CHANGELOG.md +227 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/PKG-INFO +96 -22
- {visqol_python-3.4.0 → visqol_python-3.6.0}/README.md +83 -20
- {visqol_python-3.4.0 → visqol_python-3.6.0}/pyproject.toml +20 -2
- {visqol_python-3.4.0 → visqol_python-3.6.0}/tests/test_conformance.py +50 -7
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/__init__.py +1 -1
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/__main__.py +21 -2
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/api.py +21 -3
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/gammatone.py +2 -2
- visqol_python-3.6.0/visqol/model/lattice_tcditugenmeetpackhref_ls2_nl60_lr12_bs2048_learn.005_ep2400_train1_7_raw.tflite +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/nsim.py +32 -1
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/numba_accel.py +68 -57
- visqol_python-3.6.0/visqol/quality_mapper.py +228 -0
- visqol_python-3.6.0/visqol/signal_utils.py +167 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/visqol_manager.py +49 -4
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol_python.egg-info/PKG-INFO +96 -22
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol_python.egg-info/SOURCES.txt +1 -0
- visqol_python-3.6.0/visqol_python.egg-info/requires.txt +35 -0
- visqol_python-3.4.0/CHANGELOG.md +0 -100
- visqol_python-3.4.0/visqol/quality_mapper.py +0 -124
- visqol_python-3.4.0/visqol/signal_utils.py +0 -91
- visqol_python-3.4.0/visqol_python.egg-info/requires.txt +0 -16
- {visqol_python-3.4.0 → visqol_python-3.6.0}/CONTRIBUTING.md +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/LICENSE +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/MANIFEST.in +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/setup.cfg +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/tests/test_parallel_correctness.py +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/tests/test_quick.py +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/alignment.py +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/analysis_window.py +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/audio_utils.py +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/model/libsvm_nu_svr_model.txt +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/patch_creator.py +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/patch_selector.py +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/py.typed +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol/visqol_core.py +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol_python.egg-info/dependency_links.txt +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol_python.egg-info/entry_points.txt +0 -0
- {visqol_python-3.4.0 → visqol_python-3.6.0}/visqol_python.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [3.6.0] - 2026-05-27
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Optional pyFFTW backend** (`pip install visqol-python[fftw]`):
|
|
11
|
+
- Routes `scipy.fft.fft / ifft / rfft / irfft` through FFTW3 via
|
|
12
|
+
`pyfftw.interfaces.scipy_fft` for the alignment and cross-correlation
|
|
13
|
+
FFTs. Detected at module load time in `signal_utils` and applied
|
|
14
|
+
transparently via a thin `_fft_backend()` context manager.
|
|
15
|
+
- Plan cache enabled with 60 s keep-alive so consecutive measurements
|
|
16
|
+
on equal-length signals reuse the FFTW plan.
|
|
17
|
+
|
|
18
|
+
### Improved
|
|
19
|
+
- **Fused NSIM kernel**: `_measure_patch_similarity_numba` merged its 5
|
|
20
|
+
separate 2-D convolutions (μ_r, μ_d, ref², deg², ref·deg) and the
|
|
21
|
+
intensity/structure recombination into one `(r, c)` double loop. Each
|
|
22
|
+
patch element is read from L1 once per visit instead of five times,
|
|
23
|
+
and the four intermediate `(rows × cols)` matrices are no longer
|
|
24
|
+
materialised between convs. Bit-exact with the previous split-conv
|
|
25
|
+
path (ULP-level FP rounding only).
|
|
26
|
+
- **`nsim.measure_patch_similarity`** now dispatches to the fused JIT
|
|
27
|
+
kernel when Numba is available — the same code path the DP patch
|
|
28
|
+
matcher uses, so `finely_align_and_recreate_patches` shares the
|
|
29
|
+
speedup. The pure-NumPy implementation is preserved as a fallback.
|
|
30
|
+
- **`signal_utils._hilbert`** is a drop-in `scipy.signal.hilbert`
|
|
31
|
+
replacement built on `rfft` for the real-valued input (~2× less
|
|
32
|
+
forward-transform work than the original full complex `fft`).
|
|
33
|
+
- **`signal_utils.find_best_lag`** now uses `rfft + irfft` for the
|
|
34
|
+
cross-correlation, again exploiting the real input. Net effect:
|
|
35
|
+
alignment FFTs see roughly 2× less work overall.
|
|
36
|
+
- Removed dead helper `_conv2d_boundary_valid` from `numba_accel.py`
|
|
37
|
+
(subsumed by the fused kernel).
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
- **`find_best_lag` interpreter hot loop**: the previous implementation
|
|
41
|
+
ran `xcorr_full[-max_lag:].tolist() + xcorr_full[:max_lag+1].tolist()`
|
|
42
|
+
and then `builtin argmax(list)` over a ~1.2 M-element Python list,
|
|
43
|
+
costing ~33 ms per call in pure interpreter overhead. Now uses
|
|
44
|
+
`np.concatenate + np.argmax` entirely in C.
|
|
45
|
+
|
|
46
|
+
### Performance
|
|
47
|
+
|
|
48
|
+
Apple M-series, Python 3.13, audio mode, the `guitar48_stereo` 12.5 s
|
|
49
|
+
conformance case, average of 3 runs with Numba + pyFFTW both installed:
|
|
50
|
+
|
|
51
|
+
| Stage | v3.5.0 | v3.6.0 | Speedup |
|
|
52
|
+
|--------------------|----------|----------|---------|
|
|
53
|
+
| DP Patch matching | 0.397 s | 0.131 s | **3.0×** |
|
|
54
|
+
| Global align / FFT | 0.173 s | 0.091 s | 1.9× |
|
|
55
|
+
| Fine align + NSIM | 0.093 s | 0.043 s | 2.2× |
|
|
56
|
+
| Gammatone | 0.173 s | 0.179 s | ~ |
|
|
57
|
+
| **Total** | **0.839 s** | **0.447 s** | **1.9×** |
|
|
58
|
+
| **RTF** | 0.067 | **0.036** | (C++ est. 0.093) |
|
|
59
|
+
|
|
60
|
+
### Numerical parity
|
|
61
|
+
|
|
62
|
+
All v3.5.0 conformance baselines preserved within ULP precision:
|
|
63
|
+
|
|
64
|
+
| Test | Max MOS diff vs v3.5.0 |
|
|
65
|
+
|-------------------------------|------------------------|
|
|
66
|
+
| Audio (10 conformance cases) | < 5 × 10⁻¹⁴ |
|
|
67
|
+
| Speech polynomial CA01 | 0.0 (bit-exact) |
|
|
68
|
+
| Speech lattice CA01 | 0.0 (bit-exact) |
|
|
69
|
+
|
|
70
|
+
## [3.5.0] - 2026-05-26
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
- **Deep-lattice TFLite speech quality mapper** (`pip install visqol-python[lattice]`):
|
|
74
|
+
- `TFLiteSpeechQualityMapper` loads the same `.tflite` lattice network used by
|
|
75
|
+
C++ ViSQOL's default `--use_lattice_model=true` and runs inference through
|
|
76
|
+
the upstream Google TFLite C++ runtime via `ai-edge-litert`
|
|
77
|
+
- New `use_lattice_model` parameter on `VisqolApi.create()` (default `None`
|
|
78
|
+
auto-enables lattice when the runtime is installed)
|
|
79
|
+
- New `lattice_model_path` parameter to override the bundled model
|
|
80
|
+
- New CLI flags `--no_lattice_model` and `--lattice_model PATH`
|
|
81
|
+
- New `[lattice]` and `[all]` extras in `pyproject.toml`
|
|
82
|
+
- Bundled `lattice_*.tflite` (2.1 MB) into the wheel as package data
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
- **GH issue #1**: Speech-mode MOS scores were systematically 1–2 points higher
|
|
86
|
+
than C++ ViSQOL's default. Root cause: the Python port only implemented the
|
|
87
|
+
legacy polynomial mapper (`SpeechSimilarityToQualityMapper`, equivalent to
|
|
88
|
+
C++ `--use_lattice_model=false`), while the C++ default routes through the
|
|
89
|
+
TFLite lattice network. Installing `visqol-python[lattice]` now matches C++
|
|
90
|
+
default scoring (CA01 conformance: diff 0.027 vs 1–2 MOS before).
|
|
91
|
+
- **`signal_utils.normalize()` parity bug**: the previous implementation did
|
|
92
|
+
min–max scaling to ``[0, 1]`` (shifting the signal positive and adding a DC
|
|
93
|
+
offset), while C++ ``MiscMath::Normalize`` only divides by the peak. This
|
|
94
|
+
inflated the RMS values fed to the speech-mode VAD, causing Python to keep
|
|
95
|
+
every patch as voice-active and adding spurious patches the C++ binary
|
|
96
|
+
would have discarded. Fixing this brought polynomial speech parity from
|
|
97
|
+
diff 0.007 → 0.001 and was a prerequisite for lattice parity. Only the
|
|
98
|
+
speech-mode VAD path used this function; audio mode is unaffected.
|
|
99
|
+
- **`nsim` stddev estimator mismatch**: both ``nsim.measure_patch_similarity``
|
|
100
|
+
(``np.std(..., ddof=0)``) and the Numba ``_measure_patch_similarity_numba``
|
|
101
|
+
kernel (``sqrt(ss / cols)``) used the population estimator (divide by N).
|
|
102
|
+
C++ uses Armadillo's ``stddev(..., 0)`` which is the *unbiased* sample
|
|
103
|
+
estimator (divide by N-1, despite the misleading ``0`` flag). The
|
|
104
|
+
per-band ``freq_band_stddevs`` was therefore systematically smaller by
|
|
105
|
+
``sqrt((N-1)/N) ≈ 0.974``, which fed into the pooled ``fstdnsim`` and
|
|
106
|
+
perturbed every lattice prediction.
|
|
107
|
+
- **`numba_accel.fastmath=True` on the Gammatone spectrogram kernel**: the
|
|
108
|
+
compounded LLVM-level FP reassociation across the 4-stage cascaded IIR ×
|
|
109
|
+
thousands of samples × hundreds of frames pushed lattice MOS off by
|
|
110
|
+
another ~0.02 vs strict IEEE-754. ``fastmath`` has been removed from the
|
|
111
|
+
spectrogram kernel; ``parallel=True`` is kept (each frame's IIR state is
|
|
112
|
+
independent so the reduction is safe).
|
|
113
|
+
|
|
114
|
+
### Speech-mode parity numbers (CA01 conformance)
|
|
115
|
+
|
|
116
|
+
| Mode | Before all fixes | After all fixes | C++ baseline |
|
|
117
|
+
|------|------------------|-----------------|--------------|
|
|
118
|
+
| Polynomial | diff 0.0067 | **diff 0.0011** | 3.3745 |
|
|
119
|
+
| Lattice | diff 0.0856 (≈1–2 MOS on Nils's TCD-VOIP samples) | **diff 0.0023** | 3.3130 |
|
|
120
|
+
|
|
121
|
+
### Changed
|
|
122
|
+
- Speech mode `create(mode="speech")` now auto-uses lattice when available; when
|
|
123
|
+
`ai-edge-litert` is missing, it logs a one-time warning and falls back to
|
|
124
|
+
polynomial (existing scores reproduce exactly).
|
|
125
|
+
- `tests/test_conformance.py` split the single speech case into
|
|
126
|
+
`test_speech_polynomial_conformance` (existing C++ polynomial baseline 3.3745)
|
|
127
|
+
and `test_speech_lattice_conformance` (regression baseline captured from this
|
|
128
|
+
implementation).
|
|
129
|
+
- README: documented the polynomial-vs-lattice distinction, new install matrix,
|
|
130
|
+
and parity caveats.
|
|
131
|
+
|
|
132
|
+
## [3.4.0] - 2026-03-23
|
|
133
|
+
|
|
134
|
+
### Added
|
|
135
|
+
- **Numba JIT acceleration** (`pip install visqol-python[accel]`):
|
|
136
|
+
- DP patch matching inner loops compiled to machine code via `@njit`
|
|
137
|
+
- Gammatone IIR filterbank compiled with `parallel=True` + `fastmath=True` — frames processed in parallel across all CPU cores
|
|
138
|
+
- NSIM similarity kernel JIT-compiled
|
|
139
|
+
- Automatic `NUMBA_THREADING_LAYER=workqueue` setup for macOS compatibility
|
|
140
|
+
- Zero-loss parallel accuracy (each frame's IIR state is independent)
|
|
141
|
+
- **Batch evaluation API**: `VisqolApi.measure_batch()` with optional `parallel=True` and `max_workers` for multi-process execution
|
|
142
|
+
- Exported `PatchSimilarityResult` and `ProgressCallback` from top-level package
|
|
143
|
+
|
|
144
|
+
### Performance
|
|
145
|
+
- **12x Gammatone speedup** via parallel + fastmath (1.53s → 0.13s per signal pair)
|
|
146
|
+
- **8.7x DP patch matching speedup** via Numba JIT (3.5s → 0.40s)
|
|
147
|
+
- **Overall 9x speedup**: RTF 0.58 → 0.064 (surpasses C++ estimate of 0.093)
|
|
148
|
+
- Fine alignment skip optimization: 29x speedup when lag == 0
|
|
149
|
+
|
|
150
|
+
### Improved
|
|
151
|
+
- `__repr__` / `__str__` for `SimilarityResult`, `AudioSignal`, `PatchSimilarityResult`, `Spectrogram`
|
|
152
|
+
- Logging replaces print statements in CLI verbose output
|
|
153
|
+
- Development tooling: ruff lint/format + mypy strict type checking in CI
|
|
154
|
+
|
|
155
|
+
### Fixed
|
|
156
|
+
- **CI failures**: resolved all ruff lint (308 errors), ruff format (24 files), and mypy (24 errors) issues
|
|
157
|
+
- Added `per-file-ignores` for benchmark test scripts (E402, E702)
|
|
158
|
+
- Added mypy override for `numba_accel.py` (untyped `@njit` decorators)
|
|
159
|
+
- Fixed `no-any-return` errors across `audio_utils.py`, `gammatone.py`, `visqol_core.py`, `api.py`
|
|
160
|
+
- Added `TYPE_CHECKING` imports for `ImagePatchCreator` / `VadPatchCreator` in `visqol_core.py`
|
|
161
|
+
|
|
162
|
+
## [3.3.6] - 2026-03-23
|
|
163
|
+
|
|
164
|
+
### Added
|
|
165
|
+
- **Batch evaluation API**: `VisqolApi.measure_batch()` with `progress_callback` support
|
|
166
|
+
- **Numba optional acceleration**: `visqol/numba_accel.py` with JIT-compiled DP forward pass and NSIM kernel
|
|
167
|
+
- `[accel]` optional dependency group: `pip install visqol-python[accel]`
|
|
168
|
+
|
|
169
|
+
### Improved
|
|
170
|
+
- `GammatoneFilterBank.apply_filter()` pre-builds coefficient arrays (avoids per-channel allocation)
|
|
171
|
+
- `prepare_spectrograms_for_comparison()` vectorized per-frame noise floor
|
|
172
|
+
- Ruff lint/format configuration added to `pyproject.toml`
|
|
173
|
+
- CI enhanced with lint and type-check jobs
|
|
174
|
+
- Development dependencies: `[project.optional-dependencies] dev`
|
|
175
|
+
|
|
176
|
+
## [3.3.5] - 2026-03-23
|
|
177
|
+
|
|
178
|
+
### Added
|
|
179
|
+
- **Type hints** on all public and internal APIs (`from __future__ import annotations`)
|
|
180
|
+
- **`py.typed`** marker (PEP 561) — mypy / pyright can now type-check dependents
|
|
181
|
+
- **CONTRIBUTING.md** with development setup, code style, and PR guidelines
|
|
182
|
+
- Exported `SimilarityResult` and `AudioSignal` from top-level `visqol` package
|
|
183
|
+
- `mypy` configuration in `pyproject.toml`
|
|
184
|
+
|
|
185
|
+
### Improved
|
|
186
|
+
- **Error handling**: friendly `ValueError` / `FileNotFoundError` / `TypeError` throughout:
|
|
187
|
+
- `VisqolApi.create()` now validates mode, search_window, and model_path
|
|
188
|
+
- `VisqolApi.measure()` checks file existence before processing
|
|
189
|
+
- `VisqolApi.measure_from_arrays()` validates array types, emptiness, and sample rate
|
|
190
|
+
- `AudioSignal` validates sample rate on construction
|
|
191
|
+
- `AnalysisWindow` validates sample_rate and overlap range
|
|
192
|
+
- CLI now catches exceptions and prints user-friendly error messages
|
|
193
|
+
- `AnalysisWindow.apply_hann_window()` uses `ValueError` instead of bare `assert`
|
|
194
|
+
|
|
195
|
+
## [3.3.4] - 2026-03-23
|
|
196
|
+
|
|
197
|
+
### Improved
|
|
198
|
+
- Tests rewritten in **pytest** format with `parametrize` and fixtures
|
|
199
|
+
- Added **CI workflow** (GitHub Actions): auto-test on Python 3.9–3.13 for every push/PR
|
|
200
|
+
- Added **smoke tests** (`test_quick.py`) that run without external testdata
|
|
201
|
+
- Version number now managed in a single place (`visqol/__init__.py`)
|
|
202
|
+
- Removed redundant `setup.py` — `pyproject.toml` is the single source of truth
|
|
203
|
+
- Added this CHANGELOG
|
|
204
|
+
- README: added PyPI / CI / License badges
|
|
205
|
+
|
|
206
|
+
### Fixed
|
|
207
|
+
- `requires-python` updated from `>=3.8` to `>=3.9` (numpy/scipy dropped 3.8 support)
|
|
208
|
+
|
|
209
|
+
## [3.3.3] - 2026-03-23
|
|
210
|
+
|
|
211
|
+
### Added
|
|
212
|
+
- Initial PyPI release as `visqol-python`
|
|
213
|
+
- Pure Python port of [Google's ViSQOL v3.3.3](https://github.com/google/visqol)
|
|
214
|
+
- **Audio mode** (48 kHz, SVR quality mapping) — 10/10 conformance tests pass
|
|
215
|
+
- **Speech mode** (16 kHz, exponential polynomial mapping) — 1/1 conformance test passes
|
|
216
|
+
- Python API: `VisqolApi.measure()` and `VisqolApi.measure_from_arrays()`
|
|
217
|
+
- CLI: `python -m visqol` / `visqol` command
|
|
218
|
+
- Bundled SVR model (`libsvm_nu_svr_model.txt`)
|
|
219
|
+
- GitHub Actions workflow for auto-publish to PyPI via Trusted Publisher
|
|
220
|
+
|
|
221
|
+
[3.6.0]: https://github.com/talker93/visqol-python/compare/v3.5.0...v3.6.0
|
|
222
|
+
[3.5.0]: https://github.com/talker93/visqol-python/compare/v3.4.0...v3.5.0
|
|
223
|
+
[3.4.0]: https://github.com/talker93/visqol-python/compare/v3.3.6...v3.4.0
|
|
224
|
+
[3.3.6]: https://github.com/talker93/visqol-python/compare/v3.3.5...v3.3.6
|
|
225
|
+
[3.3.5]: https://github.com/talker93/visqol-python/compare/v3.3.4...v3.3.5
|
|
226
|
+
[3.3.4]: https://github.com/talker93/visqol-python/compare/v3.3.3...v3.3.4
|
|
227
|
+
[3.3.3]: https://github.com/talker93/visqol-python/releases/tag/v3.3.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: visqol-python
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
4
4
|
Summary: ViSQOL - Virtual Speech Quality Objective Listener (Pure Python)
|
|
5
5
|
Author: Shan Jiang
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -9,7 +9,7 @@ Project-URL: Changelog, https://github.com/talker93/visqol-python/blob/main/CHAN
|
|
|
9
9
|
Project-URL: Bug Reports, https://github.com/talker93/visqol-python/issues
|
|
10
10
|
Project-URL: Source, https://github.com/talker93/visqol-python
|
|
11
11
|
Project-URL: Original C++, https://github.com/google/visqol
|
|
12
|
-
Keywords: audio-quality,speech-quality,MOS,PESQ,POLQA,visqol,objective-metric,perceptual-quality
|
|
12
|
+
Keywords: audio-quality,speech-quality,MOS,PESQ,POLQA,visqol,objective-metric,perceptual-quality,numba,batch-processing
|
|
13
13
|
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
|
|
23
23
|
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Typing :: Typed
|
|
24
25
|
Requires-Python: >=3.9
|
|
25
26
|
Description-Content-Type: text/markdown
|
|
26
27
|
License-File: LICENSE
|
|
@@ -30,6 +31,14 @@ Requires-Dist: soundfile>=0.10
|
|
|
30
31
|
Requires-Dist: libsvm-official>=3.25
|
|
31
32
|
Provides-Extra: accel
|
|
32
33
|
Requires-Dist: numba>=0.57; extra == "accel"
|
|
34
|
+
Provides-Extra: lattice
|
|
35
|
+
Requires-Dist: ai-edge-litert>=2.1.5; python_version >= "3.10" and extra == "lattice"
|
|
36
|
+
Provides-Extra: fftw
|
|
37
|
+
Requires-Dist: pyfftw>=0.13; extra == "fftw"
|
|
38
|
+
Provides-Extra: all
|
|
39
|
+
Requires-Dist: numba>=0.57; extra == "all"
|
|
40
|
+
Requires-Dist: ai-edge-litert>=2.1.5; python_version >= "3.10" and extra == "all"
|
|
41
|
+
Requires-Dist: pyfftw>=0.13; extra == "all"
|
|
33
42
|
Provides-Extra: test
|
|
34
43
|
Requires-Dist: pytest>=7.0; extra == "test"
|
|
35
44
|
Provides-Extra: dev
|
|
@@ -37,6 +46,8 @@ Requires-Dist: pytest>=7.0; extra == "dev"
|
|
|
37
46
|
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
38
47
|
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
39
48
|
Requires-Dist: numba>=0.57; extra == "dev"
|
|
49
|
+
Requires-Dist: ai-edge-litert>=2.1.5; python_version >= "3.10" and extra == "dev"
|
|
50
|
+
Requires-Dist: pyfftw>=0.13; extra == "dev"
|
|
40
51
|
Dynamic: license-file
|
|
41
52
|
|
|
42
53
|
# ViSQOL (Python)
|
|
@@ -46,19 +57,26 @@ Dynamic: license-file
|
|
|
46
57
|
[](https://pypi.org/project/visqol-python/)
|
|
47
58
|
[](LICENSE)
|
|
48
59
|
|
|
49
|
-
A pure Python implementation of [Google's ViSQOL](https://github.com/google/visqol) (Virtual Speech Quality Objective Listener)
|
|
60
|
+
A pure Python implementation of [Google's ViSQOL](https://github.com/google/visqol) (Virtual Speech Quality Objective Listener) for objective audio/speech quality assessment.
|
|
50
61
|
|
|
51
62
|
ViSQOL compares a reference audio signal with a degraded version and outputs a **MOS-LQO** (Mean Opinion Score - Listening Quality Objective) score on a scale of **1.0 – 5.0**.
|
|
52
63
|
|
|
53
64
|
## Features
|
|
54
65
|
|
|
55
66
|
- **Two modes**: Audio mode (music/general audio at 48 kHz) and Speech mode (speech at 16 kHz)
|
|
56
|
-
- **High accuracy**:
|
|
67
|
+
- **High accuracy**: 12/12 conformance tests pass against the official C++ implementation
|
|
57
68
|
- Audio mode: 9/10 tests produce **identical** MOS scores (diff = 0.000000), 1 test diff = 0.000117
|
|
58
|
-
- Speech mode: diff = 0.
|
|
59
|
-
-
|
|
60
|
-
- **
|
|
61
|
-
- **
|
|
69
|
+
- Speech mode (polynomial): diff = 0.001057
|
|
70
|
+
- Speech mode (lattice TFLite): diff = 0.002341
|
|
71
|
+
- **Two speech quality mappers** matching C++ ViSQOL:
|
|
72
|
+
- **Lattice (default)** — deep-lattice TFLite network (`--use_lattice_model=true` in C++); requires the optional `[lattice]` extra
|
|
73
|
+
- **Polynomial (fallback)** — legacy exponential fit (`--use_lattice_model=false` in C++)
|
|
74
|
+
- **Pure Python**: no C/C++ compilation required (the optional `[lattice]` extra adds the Google `ai-edge-litert` TFLite runtime as a binary wheel)
|
|
75
|
+
- **Minimal dependencies**: 4 core pip packages (`numpy`, `scipy`, `soundfile`, `libsvm-official`)
|
|
76
|
+
- **Optional Numba acceleration**: `pip install visqol-python[accel]` for JIT-compiled Gammatone filterbank (parallel) and a fused NSIM + DP patch matching kernel
|
|
77
|
+
- **Optional pyFFTW backend**: `pip install visqol-python[fftw]` routes alignment / xcorr FFTs through FFTW3 — **~16× overall speedup**, RTF 0.036 (vs C++ estimate 0.093)
|
|
78
|
+
- **Batch & parallel evaluation**: `measure_batch(parallel=True)` for multi-process execution across CPU cores
|
|
79
|
+
- **Fully typed**: PEP 561 `py.typed`, strict mypy, ruff-enforced code style
|
|
62
80
|
|
|
63
81
|
## Installation
|
|
64
82
|
|
|
@@ -66,14 +84,40 @@ ViSQOL compares a reference audio signal with a degraded version and outputs a *
|
|
|
66
84
|
pip install visqol-python
|
|
67
85
|
```
|
|
68
86
|
|
|
87
|
+
For **C++-default-equivalent speech mode** (deep-lattice TFLite mapper):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install visqol-python[lattice] # requires Python ≥ 3.10
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For **Numba-accelerated** Gammatone filtering and the fused NSIM + DP kernel:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install visqol-python[accel]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For **FFTW3-backed alignment FFTs** via pyFFTW:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pip install visqol-python[fftw]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Install everything (lattice + numba + fftw):
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pip install visqol-python[all]
|
|
109
|
+
```
|
|
110
|
+
|
|
69
111
|
Or install from source:
|
|
70
112
|
|
|
71
113
|
```bash
|
|
72
114
|
git clone https://github.com/talker93/visqol-python.git
|
|
73
115
|
cd visqol-python
|
|
74
|
-
pip install -e .
|
|
116
|
+
pip install -e ".[dev]"
|
|
75
117
|
```
|
|
76
118
|
|
|
119
|
+
> **Note on speech mode parity**: Without the `[lattice]` extra, speech mode falls back to the polynomial mapping (equivalent to running C++ ViSQOL with `--use_lattice_model=false`). The polynomial can over-predict MOS by 1–2 points on degraded speech vs the C++ default. Install `[lattice]` whenever you need numbers that line up with the C++ default behaviour (see [issue #1](https://github.com/talker93/visqol-python/issues/1)).
|
|
120
|
+
|
|
77
121
|
## Quick Start
|
|
78
122
|
|
|
79
123
|
### Python API
|
|
@@ -124,12 +168,15 @@ file_pairs = [
|
|
|
124
168
|
("ref3.wav", "deg3.wav"),
|
|
125
169
|
]
|
|
126
170
|
|
|
127
|
-
#
|
|
171
|
+
# Sequential with progress callback
|
|
128
172
|
results = api.measure_batch(
|
|
129
173
|
file_pairs,
|
|
130
174
|
progress_callback=lambda done, total: print(f"{done}/{total}"),
|
|
131
175
|
)
|
|
132
176
|
|
|
177
|
+
# Multi-process parallel (uses all CPU cores)
|
|
178
|
+
results = api.measure_batch(file_pairs, parallel=True, max_workers=4)
|
|
179
|
+
|
|
133
180
|
for pair, result in zip(file_pairs, results):
|
|
134
181
|
if isinstance(result, Exception):
|
|
135
182
|
print(f"{pair}: FAILED — {result}")
|
|
@@ -156,7 +203,10 @@ python -m visqol -r reference.wav -d degraded.wav -v
|
|
|
156
203
|
|------|-------------|
|
|
157
204
|
| `-r`, `--reference` | Path to reference WAV file (required) |
|
|
158
205
|
| `-d`, `--degraded` | Path to degraded WAV file (required) |
|
|
159
|
-
| `--speech_mode` | Use speech mode (16 kHz
|
|
206
|
+
| `--speech_mode` | Use speech mode (16 kHz) |
|
|
207
|
+
| `--no_lattice_model` | Speech mode: disable lattice TFLite mapper, use polynomial fallback |
|
|
208
|
+
| `--lattice_model` | Custom path to lattice `.tflite` model (speech mode) |
|
|
209
|
+
| `--unscaled_speech` | Don't scale polynomial speech MOS to 5.0 (polynomial only) |
|
|
160
210
|
| `--model` | Custom SVR model file path (audio mode only) |
|
|
161
211
|
| `--search_window` | Search window radius (default: 60) |
|
|
162
212
|
| `--verbose`, `-v` | Show detailed per-patch results |
|
|
@@ -184,21 +234,37 @@ The `measure()` method returns a `SimilarityResult` object with:
|
|
|
184
234
|
|
|
185
235
|
### Speech Mode
|
|
186
236
|
- Target sample rate: **16 kHz**
|
|
187
|
-
-
|
|
188
|
-
- Quality mapping: exponential polynomial fit
|
|
237
|
+
- 21 Gammatone frequency bands (50 Hz – 8 000 Hz)
|
|
189
238
|
- VAD (Voice Activity Detection) based patch selection
|
|
239
|
+
- Quality mapping (choose one):
|
|
240
|
+
- **Deep-lattice TFLite (default)** — same mapper as C++ ViSQOL's default `--use_lattice_model=true`; requires `pip install visqol-python[lattice]`
|
|
241
|
+
- **Exponential polynomial (fallback)** — same as C++ `--use_lattice_model=false`; used automatically when the lattice runtime is not installed
|
|
242
|
+
- Toggle from Python: `api.create(mode="speech", use_lattice_model=False)`
|
|
243
|
+
- Toggle from CLI: `--no_lattice_model`
|
|
190
244
|
- Best for: speech, VoIP, telephony
|
|
191
245
|
|
|
192
246
|
## Performance
|
|
193
247
|
|
|
194
|
-
Measured on Apple M-series, Python 3.13:
|
|
248
|
+
Measured on Apple M-series, Python 3.13, audio mode on the `guitar48_stereo` 12.5 s conformance case (3-run average):
|
|
195
249
|
|
|
196
|
-
|
|
|
197
|
-
|
|
198
|
-
|
|
|
199
|
-
|
|
|
250
|
+
| Configuration | RTF | Typical Time | Speedup vs pure Python |
|
|
251
|
+
|---|---|---|---|
|
|
252
|
+
| Pure Python + NumPy/SciPy | 0.58 | ~7 s | 1.0× |
|
|
253
|
+
| + `[accel]` (Numba JIT) | 0.067 | ~0.84 s | 8.7× |
|
|
254
|
+
| + `[accel] [fftw]` (Numba + FFTW3) | **0.036** | **~0.45 s** | **16×** |
|
|
200
255
|
|
|
201
256
|
> RTF (Real-Time Factor) < 1.0 means faster than real-time.
|
|
257
|
+
> With Numba + pyFFTW the Python implementation runs at **2.6× the C++ estimated speed** (C++ RTF ≈ 0.093).
|
|
258
|
+
|
|
259
|
+
Stage-level breakdown of the v3.6.0 fully-accelerated path:
|
|
260
|
+
|
|
261
|
+
| Stage | Time | % |
|
|
262
|
+
|---|---|---|
|
|
263
|
+
| Gammatone filterbank | 0.179 s | 40% |
|
|
264
|
+
| DP Patch matching (fused NSIM kernel) | 0.131 s | 29% |
|
|
265
|
+
| Global alignment (pyFFTW rfft/irfft) | 0.091 s | 20% |
|
|
266
|
+
| Fine alignment + NSIM | 0.043 s | 10% |
|
|
267
|
+
| Other (SPL, postproc, SVR, …) | 0.003 s | < 1% |
|
|
202
268
|
|
|
203
269
|
## Project Structure
|
|
204
270
|
|
|
@@ -218,18 +284,23 @@ visqol-python/
|
|
|
218
284
|
│ ├── alignment.py # Global alignment via cross-correlation
|
|
219
285
|
│ ├── nsim.py # NSIM similarity metric
|
|
220
286
|
│ ├── quality_mapper.py # SVR & exponential quality mapping
|
|
287
|
+
│ ├── numba_accel.py # Optional Numba JIT kernels (DP, NSIM, Gammatone)
|
|
221
288
|
│ ├── __main__.py # CLI entry point
|
|
289
|
+
│ ├── py.typed # PEP 561 type marker
|
|
222
290
|
│ └── model/ # Bundled SVR model
|
|
223
291
|
│ └── libsvm_nu_svr_model.txt
|
|
224
|
-
├── tests/ # Tests (pytest)
|
|
292
|
+
├── tests/ # Tests & benchmarks (pytest)
|
|
225
293
|
│ ├── conftest.py # Shared fixtures & CLI options
|
|
226
294
|
│ ├── test_quick.py # Smoke tests (no external data needed)
|
|
227
|
-
│
|
|
295
|
+
│ ├── test_conformance.py # Full conformance tests (needs testdata)
|
|
296
|
+
│ ├── test_parallel_correctness.py # Numba parallel correctness tests
|
|
297
|
+
│ └── bench_*.py # Performance benchmarks
|
|
228
298
|
├── .github/workflows/
|
|
229
|
-
│ ├── ci.yml # CI: test
|
|
299
|
+
│ ├── ci.yml # CI: lint + type-check + matrix test (Python × NumPy)
|
|
230
300
|
│ └── publish.yml # Auto-publish to PyPI on tag push
|
|
231
301
|
├── pyproject.toml # Package metadata & build config
|
|
232
302
|
├── CHANGELOG.md
|
|
303
|
+
├── CONTRIBUTING.md
|
|
233
304
|
├── LICENSE
|
|
234
305
|
└── README.md
|
|
235
306
|
```
|
|
@@ -250,7 +321,10 @@ Tested against the [official C++ ViSQOL v3.3.3](https://github.com/google/visqol
|
|
|
250
321
|
| glock_48aac | Audio | 4.3325 | 4.3325 | 0.000000 |
|
|
251
322
|
| contrabassoon_24aac | Audio | 2.3469 | 2.3468 | 0.000117 |
|
|
252
323
|
| castanets_identity | Audio | 4.7321 | 4.7321 | 0.000000 |
|
|
253
|
-
| speech_CA01 | Speech | 3.3745 | 3.
|
|
324
|
+
| speech_CA01 (polynomial) | Speech | 3.3745 | 3.3756 | 0.001057 |
|
|
325
|
+
| speech_CA01 (lattice) | Speech | 3.3130 | 3.3153 | 0.002341 |
|
|
326
|
+
|
|
327
|
+
Both speech values come from running the C++ ViSQOL binary directly with the corresponding `--use_lattice_model` flag, so they represent ground-truth parity targets.
|
|
254
328
|
|
|
255
329
|
## References
|
|
256
330
|
|
|
@@ -5,19 +5,26 @@
|
|
|
5
5
|
[](https://pypi.org/project/visqol-python/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
A pure Python implementation of [Google's ViSQOL](https://github.com/google/visqol) (Virtual Speech Quality Objective Listener)
|
|
8
|
+
A pure Python implementation of [Google's ViSQOL](https://github.com/google/visqol) (Virtual Speech Quality Objective Listener) for objective audio/speech quality assessment.
|
|
9
9
|
|
|
10
10
|
ViSQOL compares a reference audio signal with a degraded version and outputs a **MOS-LQO** (Mean Opinion Score - Listening Quality Objective) score on a scale of **1.0 – 5.0**.
|
|
11
11
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
14
|
- **Two modes**: Audio mode (music/general audio at 48 kHz) and Speech mode (speech at 16 kHz)
|
|
15
|
-
- **High accuracy**:
|
|
15
|
+
- **High accuracy**: 12/12 conformance tests pass against the official C++ implementation
|
|
16
16
|
- Audio mode: 9/10 tests produce **identical** MOS scores (diff = 0.000000), 1 test diff = 0.000117
|
|
17
|
-
- Speech mode: diff = 0.
|
|
18
|
-
-
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
17
|
+
- Speech mode (polynomial): diff = 0.001057
|
|
18
|
+
- Speech mode (lattice TFLite): diff = 0.002341
|
|
19
|
+
- **Two speech quality mappers** matching C++ ViSQOL:
|
|
20
|
+
- **Lattice (default)** — deep-lattice TFLite network (`--use_lattice_model=true` in C++); requires the optional `[lattice]` extra
|
|
21
|
+
- **Polynomial (fallback)** — legacy exponential fit (`--use_lattice_model=false` in C++)
|
|
22
|
+
- **Pure Python**: no C/C++ compilation required (the optional `[lattice]` extra adds the Google `ai-edge-litert` TFLite runtime as a binary wheel)
|
|
23
|
+
- **Minimal dependencies**: 4 core pip packages (`numpy`, `scipy`, `soundfile`, `libsvm-official`)
|
|
24
|
+
- **Optional Numba acceleration**: `pip install visqol-python[accel]` for JIT-compiled Gammatone filterbank (parallel) and a fused NSIM + DP patch matching kernel
|
|
25
|
+
- **Optional pyFFTW backend**: `pip install visqol-python[fftw]` routes alignment / xcorr FFTs through FFTW3 — **~16× overall speedup**, RTF 0.036 (vs C++ estimate 0.093)
|
|
26
|
+
- **Batch & parallel evaluation**: `measure_batch(parallel=True)` for multi-process execution across CPU cores
|
|
27
|
+
- **Fully typed**: PEP 561 `py.typed`, strict mypy, ruff-enforced code style
|
|
21
28
|
|
|
22
29
|
## Installation
|
|
23
30
|
|
|
@@ -25,14 +32,40 @@ ViSQOL compares a reference audio signal with a degraded version and outputs a *
|
|
|
25
32
|
pip install visqol-python
|
|
26
33
|
```
|
|
27
34
|
|
|
35
|
+
For **C++-default-equivalent speech mode** (deep-lattice TFLite mapper):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install visqol-python[lattice] # requires Python ≥ 3.10
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For **Numba-accelerated** Gammatone filtering and the fused NSIM + DP kernel:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install visqol-python[accel]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For **FFTW3-backed alignment FFTs** via pyFFTW:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install visqol-python[fftw]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Install everything (lattice + numba + fftw):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install visqol-python[all]
|
|
57
|
+
```
|
|
58
|
+
|
|
28
59
|
Or install from source:
|
|
29
60
|
|
|
30
61
|
```bash
|
|
31
62
|
git clone https://github.com/talker93/visqol-python.git
|
|
32
63
|
cd visqol-python
|
|
33
|
-
pip install -e .
|
|
64
|
+
pip install -e ".[dev]"
|
|
34
65
|
```
|
|
35
66
|
|
|
67
|
+
> **Note on speech mode parity**: Without the `[lattice]` extra, speech mode falls back to the polynomial mapping (equivalent to running C++ ViSQOL with `--use_lattice_model=false`). The polynomial can over-predict MOS by 1–2 points on degraded speech vs the C++ default. Install `[lattice]` whenever you need numbers that line up with the C++ default behaviour (see [issue #1](https://github.com/talker93/visqol-python/issues/1)).
|
|
68
|
+
|
|
36
69
|
## Quick Start
|
|
37
70
|
|
|
38
71
|
### Python API
|
|
@@ -83,12 +116,15 @@ file_pairs = [
|
|
|
83
116
|
("ref3.wav", "deg3.wav"),
|
|
84
117
|
]
|
|
85
118
|
|
|
86
|
-
#
|
|
119
|
+
# Sequential with progress callback
|
|
87
120
|
results = api.measure_batch(
|
|
88
121
|
file_pairs,
|
|
89
122
|
progress_callback=lambda done, total: print(f"{done}/{total}"),
|
|
90
123
|
)
|
|
91
124
|
|
|
125
|
+
# Multi-process parallel (uses all CPU cores)
|
|
126
|
+
results = api.measure_batch(file_pairs, parallel=True, max_workers=4)
|
|
127
|
+
|
|
92
128
|
for pair, result in zip(file_pairs, results):
|
|
93
129
|
if isinstance(result, Exception):
|
|
94
130
|
print(f"{pair}: FAILED — {result}")
|
|
@@ -115,7 +151,10 @@ python -m visqol -r reference.wav -d degraded.wav -v
|
|
|
115
151
|
|------|-------------|
|
|
116
152
|
| `-r`, `--reference` | Path to reference WAV file (required) |
|
|
117
153
|
| `-d`, `--degraded` | Path to degraded WAV file (required) |
|
|
118
|
-
| `--speech_mode` | Use speech mode (16 kHz
|
|
154
|
+
| `--speech_mode` | Use speech mode (16 kHz) |
|
|
155
|
+
| `--no_lattice_model` | Speech mode: disable lattice TFLite mapper, use polynomial fallback |
|
|
156
|
+
| `--lattice_model` | Custom path to lattice `.tflite` model (speech mode) |
|
|
157
|
+
| `--unscaled_speech` | Don't scale polynomial speech MOS to 5.0 (polynomial only) |
|
|
119
158
|
| `--model` | Custom SVR model file path (audio mode only) |
|
|
120
159
|
| `--search_window` | Search window radius (default: 60) |
|
|
121
160
|
| `--verbose`, `-v` | Show detailed per-patch results |
|
|
@@ -143,21 +182,37 @@ The `measure()` method returns a `SimilarityResult` object with:
|
|
|
143
182
|
|
|
144
183
|
### Speech Mode
|
|
145
184
|
- Target sample rate: **16 kHz**
|
|
146
|
-
-
|
|
147
|
-
- Quality mapping: exponential polynomial fit
|
|
185
|
+
- 21 Gammatone frequency bands (50 Hz – 8 000 Hz)
|
|
148
186
|
- VAD (Voice Activity Detection) based patch selection
|
|
187
|
+
- Quality mapping (choose one):
|
|
188
|
+
- **Deep-lattice TFLite (default)** — same mapper as C++ ViSQOL's default `--use_lattice_model=true`; requires `pip install visqol-python[lattice]`
|
|
189
|
+
- **Exponential polynomial (fallback)** — same as C++ `--use_lattice_model=false`; used automatically when the lattice runtime is not installed
|
|
190
|
+
- Toggle from Python: `api.create(mode="speech", use_lattice_model=False)`
|
|
191
|
+
- Toggle from CLI: `--no_lattice_model`
|
|
149
192
|
- Best for: speech, VoIP, telephony
|
|
150
193
|
|
|
151
194
|
## Performance
|
|
152
195
|
|
|
153
|
-
Measured on Apple M-series, Python 3.13:
|
|
196
|
+
Measured on Apple M-series, Python 3.13, audio mode on the `guitar48_stereo` 12.5 s conformance case (3-run average):
|
|
154
197
|
|
|
155
|
-
|
|
|
156
|
-
|
|
157
|
-
|
|
|
158
|
-
|
|
|
198
|
+
| Configuration | RTF | Typical Time | Speedup vs pure Python |
|
|
199
|
+
|---|---|---|---|
|
|
200
|
+
| Pure Python + NumPy/SciPy | 0.58 | ~7 s | 1.0× |
|
|
201
|
+
| + `[accel]` (Numba JIT) | 0.067 | ~0.84 s | 8.7× |
|
|
202
|
+
| + `[accel] [fftw]` (Numba + FFTW3) | **0.036** | **~0.45 s** | **16×** |
|
|
159
203
|
|
|
160
204
|
> RTF (Real-Time Factor) < 1.0 means faster than real-time.
|
|
205
|
+
> With Numba + pyFFTW the Python implementation runs at **2.6× the C++ estimated speed** (C++ RTF ≈ 0.093).
|
|
206
|
+
|
|
207
|
+
Stage-level breakdown of the v3.6.0 fully-accelerated path:
|
|
208
|
+
|
|
209
|
+
| Stage | Time | % |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| Gammatone filterbank | 0.179 s | 40% |
|
|
212
|
+
| DP Patch matching (fused NSIM kernel) | 0.131 s | 29% |
|
|
213
|
+
| Global alignment (pyFFTW rfft/irfft) | 0.091 s | 20% |
|
|
214
|
+
| Fine alignment + NSIM | 0.043 s | 10% |
|
|
215
|
+
| Other (SPL, postproc, SVR, …) | 0.003 s | < 1% |
|
|
161
216
|
|
|
162
217
|
## Project Structure
|
|
163
218
|
|
|
@@ -177,18 +232,23 @@ visqol-python/
|
|
|
177
232
|
│ ├── alignment.py # Global alignment via cross-correlation
|
|
178
233
|
│ ├── nsim.py # NSIM similarity metric
|
|
179
234
|
│ ├── quality_mapper.py # SVR & exponential quality mapping
|
|
235
|
+
│ ├── numba_accel.py # Optional Numba JIT kernels (DP, NSIM, Gammatone)
|
|
180
236
|
│ ├── __main__.py # CLI entry point
|
|
237
|
+
│ ├── py.typed # PEP 561 type marker
|
|
181
238
|
│ └── model/ # Bundled SVR model
|
|
182
239
|
│ └── libsvm_nu_svr_model.txt
|
|
183
|
-
├── tests/ # Tests (pytest)
|
|
240
|
+
├── tests/ # Tests & benchmarks (pytest)
|
|
184
241
|
│ ├── conftest.py # Shared fixtures & CLI options
|
|
185
242
|
│ ├── test_quick.py # Smoke tests (no external data needed)
|
|
186
|
-
│
|
|
243
|
+
│ ├── test_conformance.py # Full conformance tests (needs testdata)
|
|
244
|
+
│ ├── test_parallel_correctness.py # Numba parallel correctness tests
|
|
245
|
+
│ └── bench_*.py # Performance benchmarks
|
|
187
246
|
├── .github/workflows/
|
|
188
|
-
│ ├── ci.yml # CI: test
|
|
247
|
+
│ ├── ci.yml # CI: lint + type-check + matrix test (Python × NumPy)
|
|
189
248
|
│ └── publish.yml # Auto-publish to PyPI on tag push
|
|
190
249
|
├── pyproject.toml # Package metadata & build config
|
|
191
250
|
├── CHANGELOG.md
|
|
251
|
+
├── CONTRIBUTING.md
|
|
192
252
|
├── LICENSE
|
|
193
253
|
└── README.md
|
|
194
254
|
```
|
|
@@ -209,7 +269,10 @@ Tested against the [official C++ ViSQOL v3.3.3](https://github.com/google/visqol
|
|
|
209
269
|
| glock_48aac | Audio | 4.3325 | 4.3325 | 0.000000 |
|
|
210
270
|
| contrabassoon_24aac | Audio | 2.3469 | 2.3468 | 0.000117 |
|
|
211
271
|
| castanets_identity | Audio | 4.7321 | 4.7321 | 0.000000 |
|
|
212
|
-
| speech_CA01 | Speech | 3.3745 | 3.
|
|
272
|
+
| speech_CA01 (polynomial) | Speech | 3.3745 | 3.3756 | 0.001057 |
|
|
273
|
+
| speech_CA01 (lattice) | Speech | 3.3130 | 3.3153 | 0.002341 |
|
|
274
|
+
|
|
275
|
+
Both speech values come from running the C++ ViSQOL binary directly with the corresponding `--use_lattice_model` flag, so they represent ground-truth parity targets.
|
|
213
276
|
|
|
214
277
|
## References
|
|
215
278
|
|