fullseye

色・芸術・拡張 — 使い方ガイド

この族は何をする道具箱か

この族は「白黒 1 枚」の外側に踏み出す 4 系統の 2-D オペレータ箱です。(1) color は 1 チャンネルの image (H×W) を第一級の color (H×W×3, RGB, [0,1]) sort に持ち上げて扱う HALCON 由来の色オペ群 — 色空間変換・3×3 チャンネル混合・チャンネル PCA・輝度化・チャンネル取り出し。gray から color への橋渡しは cfa_to_rgb(Bayer デモザイク)が一手に担い、rgb1_to_gray / access_channel が color を gray に戻します。(2) artistic は非写実(NPR)フィルタ(OpenCV の stylization / pencil sketch、PIL の emboss)。(3) augmentation(aug_)は理想画像を「実カメラが吐く画像」に劣化させる sim-to-real / ドメインランダム化 — ショット/読み出し/固定パターンノイズ、モーションブラー、周辺減光、色収差、ローリングシャッター、JPEG ブロック、cutout、レンズ歪み。(4) extra(xsitk_)は SimpleITK 由来で HALCON に無い曲率フロー拡散・再構成モルフォロジ・領域拡張/エントロピー閾値・符号付き距離場。

呼び出しは全 op 共通で「1 枚の配列 + 2 つのスカラつまみ a, b ∈ [0,1]」: fullseye.apply(img, "op名", a, b)。ただし sort(型)は繋げると変わりますimage → color(cfa_to_rgb)、color → image(rgb1_to_gray)、image → region(xsitk_maxentropy_thresh)、region → image(xsitk_signed_maurer_dist)。augmentation は入出力とも image。augmentation/extra のノイズ・パターン・位置は つまみ (a,b) からシードされた固定実現で、同一入力なら 2 回呼んでも bit 完全一致します(再現可能ホールドアウト評価のための registry 契約。1 回の呼び出しは 1 つの固定実現なので、ノイズ集合を見たければつまみを掃引します)。

代表的なパイプライン(op の繋がり)

色 sort への持ち上げ → 色空間で加工 → gray へ戻す、という色の道筋:

flowchart LR
  I["image (H×W)"] -->|cfa_to_rgb| C1["color (H×W×3)"]
  C1 -->|trans_from_rgb| C2["color (HSV/Lab/YUV/XYZ)"]
  C2 -->|trans_to_rgb| C3["color (RGB)"]
  C1 -->|linear_trans_color / principal_comp| C4["color (混合 / PCA)"]
  C3 -->|rgb1_to_gray| G["image (輝度)"]
  C4 -->|access_channel| G2["image (1ch)"]

理想画像を実カメラ風に劣化させ(sim-to-real)、拡張スムージングで整え、閾値で前景を切り、距離場にする道筋:

flowchart LR
  S["image (理想 / 合成)"] -->|aug_motion_blur| A1["image"]
  A1 -->|aug_shot_noise| A2["image"]
  A2 -->|aug_vignette| A3["image (実カメラ風)"]
  A3 -->|xsitk_curvature_flow| E1["image (エッジ保存平滑)"]
  E1 -->|xsitk_maxentropy_thresh| R["region (二値前景)"]
  R -->|xsitk_signed_maurer_dist| D["image (符号付き距離)"]

使い方(op グループ別)

各行 = op名 — 何をするか — 呼び出し例。つまみの意味は grounded(実装準拠)。

color(8): image ⇄ color の橋渡しと色加工

artistic(3): 非写実(NPR)フィルタ

augmentation(10): 実カメラ劣化の合成(sim-to-real)

extra(14): SimpleITK 由来(HALCON に無い拡張)

エッジ保存平滑・再構成モルフォロジ(image → image):

閾値・領域拡張(image → region)と距離場(region → image):

動く最小例(検証済み gallery2d_color_artistic から)

repo 直下で py -3.11 this.py。検証済みギャラリーの GT を凝縮した自己完結・GT アサート付きスニペット(sort の橋渡し・BT.601 輝度の厳密一致・チャンネル選択・cos⁴ 減光・cutout・最大エントロピー閾値を確認):

# repo 直下で: py -3.11 this.py
import numpy as np
import fullseye

_N = 48

def make_image(n=_N):
    yy, xx = np.mgrid[0:n, 0:n].astype(np.float64)
    grad = xx / (n - 1)
    disk = ((yy - n * 0.35) ** 2 + (xx - n * 0.4) ** 2) < (n * 0.18) ** 2
    checker = ((xx.astype(int) // 6 + yy.astype(int) // 6) % 2) * 0.15
    return np.clip(0.35 * grad + 0.45 * disk + checker, 0.0, 1.0)

def make_color(n=_N):
    g = make_image(n)
    return np.clip(np.stack([g, 0.7 * g + 0.1, 1.0 - g], -1), 0.0, 1.0)

img = make_image()
col = make_color()

# 1) cfa_to_rgb: 1 枚のグレー画像 (H,W) から色 sort (H,W,3) への橋渡し
rgb = fullseye.apply(img, "cfa_to_rgb", 0.0, 0.0)
assert rgb.ndim == 3 and rgb.shape[-1] == 3, "cfa_to_rgb must yield H x W x 3"
assert rgb.min() >= -1e-6 and rgb.max() <= 1 + 1e-6, "color stays in [0,1]"

# 2) rgb1_to_gray: 輝度 0.299R + 0.587G + 0.114B に厳密一致 (BT.601)
gray = fullseye.apply(col, "rgb1_to_gray", 0.5, 0.4)
expected = 0.299 * col[..., 0] + 0.587 * col[..., 1] + 0.114 * col[..., 2]
assert np.allclose(gray, expected, atol=1e-12), "luminance must be exact BT.601"

# 3) access_channel: つまみ a でチャンネルを選ぶ (a=0->ch0, a=1->ch2)
ch0 = fullseye.apply(col, "access_channel", 0.0, 0.0)
ch2 = fullseye.apply(col, "access_channel", 1.0, 0.0)
assert np.array_equal(ch0, col[..., 0]) and np.array_equal(ch2, col[..., 2])
assert not np.array_equal(ch0, ch2), "different channels must differ"

# 4) aug_vignette: cos^4 減光 — 隅は中心よりはっきり暗い(一様入力で透過率を可視化)
one = np.ones((_N, _N), np.float64)
vig = fullseye.apply(one, "aug_vignette", 1.0, 0.5)
center, corner = float(vig[_N // 2, _N // 2]), float(vig[0, 0])
assert corner < 0.5 * center, "vignette corner must be darker than centre"

# 5) aug_cutout: 矩形オクルージョンで零画素が増える(b<=0.5 -> 黒で消去)
cut = fullseye.apply(img, "aug_cutout", 0.4, 0.3)
assert int((cut == 0.0).sum()) > int((img == 0.0).sum()) + 100

# 6) xsitk_maxentropy_thresh: 二値かつ前景/背景を分離(自明な全0/全1でない)
seg = fullseye.apply(img, "xsitk_maxentropy_thresh", 0.5, 0.4)
uniq = np.unique(seg)
assert np.all((uniq == 0.0) | (uniq == 1.0)), "threshold output must be binary"
assert 0.0 < float(seg.mean()) < 1.0, "must separate foreground from background"

print("PASS")

数式(必要な op のみ)

輝度化 rgb1_to_gray / rgb3_to_gray(BT.601)。ギャラリーは atol=1e-12 で厳密一致を課します:

\[Y = 0.299\,R + 0.587\,G + 0.114\,B\]

周辺減光 aug_vignette。正規化半径 $r$(中心 0・隅 1)と減光半径 $R=0.35+1.15b$ で、透過率は自然な cos⁴ 則、これを強度 $a$ でブレンド:

\[t(r) = \cos^4\!\big(\arctan(r/R)\big) = \frac{1}{\big(1+(r/R)^2\big)^2}, \qquad \text{out} = v\,\big(1 - a + a\,t(r)\big)\]

ショットノイズ aug_shot_noise。画像を光子率とみなし、光子スケール $K=5+250(1-a)$ でポアソン標本化して戻す(SNR $\sim\sqrt{K}$):

\[\tilde v = \frac{\mathrm{Poisson}(vK)}{K}\]

レンズ歪み aug_barrel。正規化半径 $r$(隅 1)に多項式歪み、$k=0.6a$($b<0.5$ で $+k$= 樽型、$b\ge0.5$ で $-k$= 糸巻き型):

\[r_{\text{src}} = r\,\big(1 + k\,r^2\big)\]

符号付き距離場 xsitk_signed_maurer_dist。符号付き Euclidean 距離 $d$(領域内で負)を tanh で [0,1] に圧縮(スケール $a$)、内部 $<0.5<$ 外部:

\[\text{out} = 0.5 + 0.5\,\tanh\!\Big(\frac{d}{1 + 9a}\Big)\]

サンプルデータ

デバッグ用の 2-D 画像源は ../../SAMPLES.md を参照(同梱せずユーザー DL 方式、fail-closed)。この族には合成の checker_noisy / gradient(色の橋渡しやノイズ拡張の効きを可視化しやすい)と、skimage.datacoins / camera(NPR フィルタ・閾値・領域拡張の実写題材)が向きます — import sample_images; sample_images.load("camera")。cfa_to_rgb 以降の color op は 1 枚の gray からその場で色 sort を作れるので、追加素材なしでも動きます。

参考文献(正典)

台帳は ../../../REFERENCES.md。この族のアルゴリズムの古典:


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