fullseye

画素分類・領域成長・マーカー分水嶺(HALCON Segmentation 章の 9 op) — 使い方ガイド

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

グレー値・特徴空間から領域を作る層です。入力は画像 1 枚(image2d)、2 枚、 または多チャネルの特徴画像(images = 2-D の list)と、参照領域・学習済みクラス・ マーカー画像。出力は画素ごとの真偽(mask)かラベル画像(labels2d)。

HALCON の “Segmentation” 章にある 9 演算子の genuine 実装(numpy + scipy、分水嶺だけ skimage)で、segmentation.py が実体、opssegmentation.py が台帳です。

9 op / 4 カテゴリ:

なぜ台帳に載せたか —— 在庫を数えた結果

segmentation.py は 2026-08 から wheel に同梱され、fullseye.vision.segment.<名前> (HALCON 名の facade)からは届いていました。しかし fullseye.ledger / fullseye.op / fullseye.<名前> のどれにも無く、tests/test_public_reachability.py の数え方では 9 本とも「公開経路から呼べない関数」でした。examples/poc_cell_counting.py と examples/poc_particle_sizing.py は揃って「2-D の分水嶺が公開経路に無い」を道具の穴に 挙げ、docs/ops/blob/guides/blob_analysis.md も同じ行を持っていました。

引いた先 何が在ったか なぜ代わりにならないか
fullseye.op.watersheds / regiongrowing / regiongrowing_mean 2-D レジストリの分水嶺・領域成長 画像 1 枚 + a/b ノブの形で、マーカー画像・参照画像・学習済みクラスを渡せない
fullseye.ledger.blob_split 距離の降順で回す分水嶺 目的は同じだが別算法(numpy/scipy だけで閉じる)。blob2d の docstring が「skimage 不在時に黙って別算法に落ちる watersheds_marker は使わない」と書いている —— 同じ台帳に混ぜると「割る」が同名の別物を 2 本持つ
fullseye.vision.segment.watersheds_marker facade(統一 registry) HALCON 名で引く層で、docs/ops のノート・連鎖ファザー・op_run の入力補助には乗らない

流れ

flowchart TD
    IMG["image2d (H, W)"]
    REF["基準画像 image2d"]
    IMG --> CD["check_difference<br/>|a − b| > tol"]
    REF --> CD
    CD --> M1["mask"]
    F1["特徴 1 image2d"] --> SUP["class_2dim_sup<br/>参照領域の箱"]
    F2["特徴 2 image2d"] --> SUP
    RR["参照領域 mask"] --> SUP
    SUP --> M2["mask"]
    F1 --> UNS["class_2dim_unsup<br/>k-means"]
    F2 --> UNS
    UNS --> L1["labels2d (0..k−1)"]
    X["特徴ベクトル群 matrix (N, D)"] --> LN["learn_ndim_norm"]
    LN --> MODEL["table {mean, cov, inv}"]
    FS["特徴画像 images (D 枚)"] --> CN["class_ndim_norm<br/>マハラノビス距離 < thresh"]
    MODEL --> CN
    CN --> M3["mask"]
    IMG --> LUT["classify_image_class_lut"]
    SIG["LUT signal"] --> LUT
    LUT --> L2["labels2d"]
    IMG --> EG["expand_gray<br/>種から |Δ| < tol で膨張"]
    SEED["種 mask"] --> EG
    EG --> M4["mask"]
    FS --> RG["regiongrowing_n"]
    RG --> L3["labels2d (1..n)"]
    IMG --> WS["watersheds_marker"]
    MK["markers labels2d"] --> WS
    WS --> L4["labels2d(マーカー番号)"]

最短の使い方

import numpy as np
import fullseye as fs

rng = np.random.default_rng(0)
im = rng.random((64, 64))
im[16:48, 16:48] = 0.5 + rng.normal(0.0, 0.01, (32, 32))        # 平坦な島

# 種から育てる(|im − 種の平均| < tol の 4 連結成分)
seed = np.zeros((64, 64), bool)
seed[30:34, 30:34] = True
island = fs.ledger.expand_gray(im, seed, tol=0.05)

# マーカー制御の分水嶺(尾根で割れる)
y, x = np.mgrid[0:64, 0:64]
valley = -np.abs(x - 32).astype(float)
markers = np.zeros((64, 64), int)
markers[32, 4] = 1
markers[32, 60] = 2
basins = fs.ledger.watersheds_marker(valley, markers)             # 左 = 1、右 = 2

# N 次元の正規分布クラス: 学習 → 分類
X = rng.normal(size=(500, 2)) * np.array([2.0, 0.5])
model = fs.ledger.learn_ndim_norm(X)
inside = fs.ledger.class_ndim_norm([im, im ** 2], model, thresh=2.0)

罠(実測、直していない)

同じ台帳の他のカテゴリ(採点・真値つき世界・動的輪郭・グラフと階層・閾値・3-D 分水嶺)

この台帳(opssegmentation)には HALCON 章の 9 op の後に、分けた結果を採点する側・真値を作る側・輪郭で分ける側・ グラフと階層・閾値の定理で分ける側が同居している。詳しくは 分割の採点と真値つき合成世界、 動的輪郭とレベルセット、 グラフ・階層・閾値の定理でつくる分割。

カテゴリ op
score(採点) seg_confusion_table / seg_dice_jaccard / seg_boundary_f / seg_hausdorff / seg_mean_surface_distance / seg_under_over_segmentation / seg_object_counts_match / seg_score_card
world(真値つき合成世界) world_blobs_touching / world_grains_voronoi / world_parts_with_shadow / world_texture_regions / world_gradient_illumination / world_thin_structures / lens_area / voronoi_cells
contour(動的輪郭・レベルセット) snake_evolve / gvf_field / chan_vese_energy / chan_vese_evolve / morph_chan_vese / morph_geodesic_ac / edge_stop_g / level_set_reinit / drle_evolve / curvature_flow
graph(グラフ・階層・超画素) graph_cut_binary / alpha_expansion / statistical_region_merging / max_tree / area_opening_attr / quasi_flat_zones / alpha_tree / hierarchical_watershed / ultrametric_contour_map / snic_superpixels / quickshift / superpixel_quality
threshold(閾値の 4 定理) threshold_triangle / threshold_isodata / threshold_kittler / threshold_kapur
watershed3d(接触した 3-D 物体を割る) distance_peaks / watershed_vol / separate_touching

watershed3d は 2-D の watersheds_marker の 3-D 版ではなく、距離変換の極大をシードにして接した物体を割る 定型(volops の距離変換・極大・分水嶺の薄い合成 + skimage が無いときの純 scipy の代替)。入口は voxel (2 値の (D, H, W))、出口は labels(3-D の int ラベル)。門は鏡映対称な 2 球で体積が厳密に等しいこと、 scipy と skimage の 2 実装が同じ分割になること(tests/test_watershed3d_ledger.py)。

import fullseye as fs
lab = fs.ledger.separate_touching(volume > 0, min_distance=5.0)   # 接した粒を 1 個ずつのラベルに
seeds = fs.ledger.distance_peaks(volume > 0, min_distance=5.0)     # 割る前にシードの数を見る

走る例: py -3.11 examples/watershed3d_tour.py(接した 2 球が連結成分では 1 個、分水嶺では体積の等しい 2 個)。

HALCON 側の op で分けた結果は、そのまま seg_score_card に真値と一緒に渡せる(背景 0 の約束だけ揃える)。

関連