fullseye

傷や突起に引きずられない平均線を引く(ロバストなガウス回帰・スプラインフィルタ、断面と面)

できること

粗さは断面(または面)から平均線(うねり)を引いて残りを測ります。普通のガウスフィルタは深い傷に引かれて平均線が傷の側へ凹み、傷の両側の健全な面が山に見えます(PoC の実測で、傷の脇の粗さが真値の 2.5 倍)。roughness 族の新しい 3 本は、Tukey の biweight で傷の重みを下げながら反復し、平均線を傷から守ります。

実測(合成した断面、真のうねりは解析的): 傷の標本が 20 % までロバストの引きずりは線形の 0.06 倍、40 % で破綻します(理論上は 50 %。線形解から反復を始めるので手前で崩れる)。収束しなければ値を返さず拒否します。

What it does

Three ops in the roughness ledger (category prepare). robust_gaussian_profile is a Gaussian-weighted local regression (order 0 or 1) iterated with Tukey’s biweight at c · median|r|; its transmission is exactly 50 % at λc (closed form exp(-π(αλc/λ)²), α² = ln2/π), measured to 3e-8. robust_spline_profile solves a second-difference penalised least squares (a discrete cubic smoothing spline) with the same reweighting; its transfer function 1/(1+(sin(π dx/λ)/sin(π dx/λc))⁴) is matched to 1e-11. robust_gaussian_surface is the areal version. On a synthetic profile with a known waviness, scratches covering up to 20 % of the samples drag the robust mean line 0.06 times as far as the linear one; at 40 % it breaks down, and a non-converging iteration is refused rather than returned.

向くところ / 向かないところ

向く: スタイラス計・白色干渉計の断面や面で、傷・ゴミ・突起が混じるうねりの分離。平均線の引きずり量を数えたいとき(robust=False と並べる)。

向かない: ★外れ値の無いデータでも biweight は雑音の裾を刈るので線形とは一致しません(「重み 1 なら既存の surface_filter と 1e-12 で一致」が正しい言い方)。傷が窓(既定 ±λc)より広いと外挿せず拒否します。汚染が 4 割を超えると破綻します。次数 2 は、カットオフでの透過率が 85 % になりカットオフの意味が変わるので出していません。既存の surface_filter は規格の文面の打ち切り ±λc/2 のせいで λc での透過率が 0.5095 でしたが、2026-10-18 に ±0.75λc へ直して 0.5000 になりました(この族の新 op は ±λc で 0.5)。実際のスタイラス断面での検証は未了で、真値は閉形式と合成したうねりです。

推奨パイプライン

robust_gaussian_profile → profile_params

断面の傾きや形状を先に除き(面なら surface_form_remove)、robust_gaussian_profile の粗さを profile_params(Ra / Rq / Rz など)へ渡す。info["converged"] と info["n_zero_weight"](重みを 0 にされた標本の数)を見て、傷として扱われた点の多さを確認する。端は削らず全長が返るので、端の信頼度が要るなら両端 λc/2 を捨てる。

代替

端の自然境界条件と鋭い遮断が欲しいときは robust_spline_profile、面は robust_gaussian_surface。外れ値が無いと分かっていて普通の線形ガウスでよいときは surface_filter(打ち切り ±0.75λc、λc の透過率 0.5000)か、robust=False, order=0, truncate=0.75 の線形版(内部で 1e-12 一致)。

限界

実寸校正

dx と lambda_c は同じ長さの単位で渡す(混ぜても検出できない)。λc は測定条件(評価長さ・粗さの区分)で決まる値で、この op は決めない。既知波長 λc の正弦を robust=False で通して振幅 0.5 を確かめると、設定と単位の取り違えを現場で拾える。

最初の 1 本

import numpy as np
import roughness

x = np.arange(2400.0)
truth = 2.0 * np.sin(2 * np.pi * x / 600.0)                  # 本当のうねり(µm)
z = truth + 0.3 * np.random.default_rng(0).standard_normal(x.size)
for s in (500, 900, 1300, 1700):
    z[s:s + 8] -= 6.0                                          # 深い傷 4 本
_, lin, _ = roughness.robust_gaussian_profile(z, 1.0, 80.0, robust=False)
rough, rob, info = roughness.robust_gaussian_profile(z, 1.0, 80.0)
inner = slice(150, -150)
print("引きずり 線形 %.2f / ロバスト %.2f µm(反復 %d 回)" % (
    np.abs(lin - truth)[inner].max(), np.abs(rob - truth)[inner].max(), info["iterations"]))