fullseye

tb_apply_weighting — 2D typed op

tb_apply_weighting: input → output

図は合成の入力 128×128 で実際に走らせた出力。左が入力、右が出力。点群は上から見た散布(明るさ = z)、1-D 列は折れ線、体積は z 方向の最大値投影、動画は中央フレーム、複素画像は振幅、絵にならない返り値は値そのもの。

つまみ a は出力を変えない(実測: 0.1 / 0.5 / 0.9 で同一)。

つまみ b は出力を変えない(実測: 0.1 / 0.5 / 0.9 で同一)。

段階(前置きの op → この op。左から順):

tb_apply_weighting: stages

別の画像でも(合成シーン / 写真 / 硬貨。上段が入力、下段がその出力。つまみは既定):

tb_apply_weighting: other inputs

使い方

Apply an A / C / Z frequency weighting to a signal, zero-phase.

The weighting is applied as a real, even gain in the frequency domain, so it
introduces no phase distortion and no group delay — the result is aligned
sample-for-sample with the input, which a recursive filter implementation
would not be.

Measured: a 1 kHz sine at 16 kHz (16000 samples, exactly 1000 periods) is
returned **unchanged** by both A and C weighting — max absolute difference
1.078e-13 for A and 1.225e-13 for C — because both curves are exactly 0 dB
at 1 kHz by construction. A 100 Hz sine of amplitude 1.0 comes back with
amplitude **0.110373** under A weighting, against the closed form
``10**(-19.1428/20) = 0.110373``.

``kind="Z"`` returns a copy, unchanged.

**A tone that is not a whole number of periods in the record reads too
loud, by up to 17 dB, and nothing raises.** The multiplication is over the
record's own DFT, which treats it as periodic; a tone that does not close
on itself leaks across every bin. That leakage would be harmless if the
weighting were flat, but A weighting spans about 40 dB between 20 Hz and
1 kHz, so a sidelobe 40 dB below a 31.5 Hz tone arrives at 1 kHz weighted
40 dB *higher* and takes over the sum. Measured, 0.5 s at 48 kHz, error
against the closed-form ``A(f)`` for a pure tone:

==========  =============  ==========  ==============
f (Hz)      periods        error (dB)  bin-centred?
==========  =============  ==========  ==============
22.0        11.0           **+0.0000**  yes
31.5        15.75          **+7.7986**  no
20.5        10.25          **+17.2116** no (worst, 20-200 Hz)
63.0        31.5           +0.1121      no
100.0       50.0           +0.0000      yes
1000.0      500.0          -0.0000      yes
==========  =============  ==========  ==============

**31.5 Hz is a nominal one-third-octave centre**, so this is a path a real
measurement walks into rather than a contrived one. The error is always
*positive* — leakage only ever adds power at frequencies the curve favours.

Two things confirm the diagnosis is dynamic range and not arithmetic. The
same 31.5 Hz tone under **C** weighting, whose tilt over the same span is a
few dB rather than forty, is off by only **+0.0493 dB**. And lengthening the
record to where the tone *does* close on itself removes it entirely: at
31.5 Hz the error is +7.7524 dB over 0.25 s, +7.7986 over 0.5 s, +0.4615
over 1 s, and **-0.0000** over 2 s and 4 s (63 and 126 whole periods).

Two candidate cures were measured (error in dB against the closed form,
0.5 s at 48 kHz):

===================================  ========  ========  ========
treatment                            31.5 Hz   20.5 Hz   22.0 Hz
===================================  ========  ========  ========
as implemented (rectangular)         +7.7986   +17.2116  +0.0000
zero-pad x4 (linear convolution)     +5.5620   +14.3352  +0.7969
Hann window, corrected for its gain  **+0.0534**  **+0.1841**  +0.1505
===================================  ========  ========  ========

Padding barely helps — zero-padding a tone puts an abrupt edge into the
record and an edge is broadband. **A Hann window does essentially cure it**,
turning +17 dB into +0.18 dB, at the cost of the bin-centred columns which
go from exactly 0 to about 0.15 dB. So why is it not the default?

**Because it would trade a loud error for a quiet one.** ``L_eq`` is an
*energy average over the record*, and a window is not energy-preserving for
anything that is not stationary. Measured with Z weighting (so the window is
the only thing acting) on a 50 ms 1 kHz burst inside a 0.5 s record, all
three placements being ``-13.0103`` dB unwindowed as they must be:

==============  ============  ===========
burst position  Hann (dB)     difference
==============  ============  ===========
start           -36.0587      **-23.05**
centre          -8.8218       +4.19
end             -36.0587      **-23.05**
==============  ============  ===========

A window makes the answer depend on *where in the record the sound happened*,
which is precisely the "plausible wrong number" this module refuses to ship
by default. So the rectangular behaviour stays, and the Hann estimate is
available by asking for it: ``equivalent_level(..., window="hann")``. Use it
when the record is stationary and tonal — which is exactly when the leakage
bites — and never when the level of a transient is the point.

A cure with neither cost is a different implementation entirely: the
standard cascade of A-weighting biquads in the time domain, which would give
up the exact-0-dB-at-1-kHz-by-construction property this function is built
on, and the zero group delay promised above.

**Also worth doing**: give the analysis enough record that the content is
many periods long, prefer durations that are whole multiples of the period
you care about, and read a low-frequency A-weighted level from
:func:`octave_spectrum` (which reports per-band power, so leakage is visible
as energy in bands where none belongs) rather than from a single number.

**Raises** ``ValueError``: everything :func:`_as_signal` refuses, an unknown
``kind``, ``rate <= 0``.

Typed bridge of the acoustics op apply_weighting into the 2-D evolution registry: the same implementation, called under the op(v, a, b) convention. This op has no tunable parameter; a and b are unused.

参考(サンプルデータ・文献)

Studio で試す

下のプログラムは実際に走ることを確かめてある(図と同じ入力)。Studio のヘルプではこのブロックがボタンになり、その場で読み込んで実行できる。

img_to_signal 0.50 0.50
tb_apply_weighting 0.50 0.50

実行できる例(この op を実際に呼ぶ検証済みサンプル)

次の例は元の台帳 op apply_weighting を呼ぶもの。この橋渡し op は同じ実装を fn(v, a, b) 規約に合わせただけなので、挙動はそのまま当てはまる(呼び出し形だけ違う)。

型が繋がる次の op(signal を入力に取れる)

identity · tb_create_funct_1d_array · tb_smooth_funct_1d_gauss · tb_smooth_funct_1d_mean · tb_derivate_funct_1d · tb_integrate_funct_1d · tb_zero_crossings_funct_1d · tb_abs_funct_1d

同カテゴリ(typed)

tb_points_to_voxel · tb_estimate_point_normals · tb_iss_keypoints · tb_project_points · tb_render_point_depth · tb_statistical_outlier_removal · tb_radius_outlier_removal · tb_voxel_grid_downsample


Provenance: ops.py — 2D operator registry. この per-op ノートは tools/opdocs.py md が自動生成(手編集しない)。

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.