image-to-svg について
ピクセルに基づく色量子化と輪郭抽出で、ラスター画像を SVG として再現する Python パイプラインです。塗りと線画を組み合わせる方式、パレットプリセット、バッチ生成を備え、制作者や開発者向けです。
主な機能
- 01ラスター画像を SVG に変換
- 02画像種別に応じたモード選択
- 03線画を SVG のストロークとして抽出
- 04パレットプリセットによる色の置換
- 05複数バリエーションの一括生成
活用シーン
- 01ロゴやアイコンのベクター化
- 02絵画や写真の SVG 化
- 03ポップアート風パレットの作成
- 041枚の画像から複数スタイルを生成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ oaustegard/ claude-skills --skill image-to-svgインストール後、Claude Code で/image-to-svgを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CHANGELOG.mdscripts/lines.pynn_assign.cpipeline.py詳細
画像からの SVG 再現
データ駆動型の色量子化と輪郭抽出を使い、ラスター画像を忠実な SVG として再現します。視覚的な解釈に基づいて図形を手描きすることは絶対に避けてください — 実際のピクセルデータから必ずジオメトリを抽出してください。
基本原則
想像ではなく、データを信頼してください。 画像の色の正確な一致、図形の配置、空間的な関係について、Claude による視覚的な解釈は信頼できません。すべての図形、色、位置は、元画像のピクセルを計算で分析して求める必要があります。
クイックスタート
pip install opencv-python-headless scikit-image scipy scikit-learn --break-system-packages -q
apt-get install -y librsvg2-bin -qq
import sys
sys.path.insert(0, '/mnt/skills/user/image-to-svg/scripts')
from pipeline import image_to_svg
svg, flow = image_to_svg("source.jpg", mode="painting")
with open("output.svg", "w") as f:
f.write(svg)
flow.summary() # timing + status per step
モードの選択
画像を見て、「滑らかなグラデーションがありますか、それとも輪郭がはっきりしていますか?」と確認してください。グラデーションがある場合は K を大きくし、輪郭がはっきりしている場合は K を小さくします。
| モード | K | 最適な用途 | 暗色図形の判定 |
|---|---|---|---|
"graphic" | 28 | ロゴ、アイコン、Kandinsky、フラットデザイン | 緩め(細い線を保持) |
"illustration" | 40 | コミック、エディトリアル、デジタルアート | 中程度 |
"painting" | 56 | ルネサンス、印象派、水彩画 | 標準 |
"photo" | 64 | ポートレート、風景、静物画 | 標準(木版画のようなアーティファクトを防止) |
デフォルトは "painting" です。判断に迷ったら、まずこのモードを使ってください。
トレードオフ:K=64 では約 2300 個の図形(SVG 約 1.2MB)、K=28 では約 1000 個の図形(約 550KB)が生成されます。これは v1.9.0 のコンパクト出力より前の測定値で、コンパクト出力は約 2.6 倍小さくなります(出力形式を参照)。K を上げると処理時間はおよそ 2 倍になります。写真では階調表現の品質が大幅に向上しますが、グラフィックアートではその効果は生かされません。
すべてのモードのデフォルト値(K、dark_lum、compactness_min など)は **kwargs で上書きできます。
svg, flow = image_to_svg("source.jpg", mode="graphic", K=12, min_area=20)
コンポジションパイプライン(線画)
線、ストローク、幾何学的な図形が主体の画像(Kandinsky、建築図面、技術イラスト、コミックのインク画など)では、標準の塗りつぶしのみのパイプラインを使うと、きれいなストロークではなく、ギザギザの塗りつぶされたポリゴンが生成されます。コンポジションパイプラインでは、次の 2 つのパスでこの問題を解決します。
パス 1 — 線の抽出:モルフォロジー収縮で細い特徴を分離 → スケルトン化して 1px の中心線を抽出 → Hough 線検出 → 同一直線上の断片を結合 → ストローク幅を測定 → 色をサンプリングします。stroke-width を持つ SVG の <line> 要素を出力します。
パス 2 — 塗りつぶしの抽出:画像から線の領域を除去(メディアンブラーで局所的な背景の推定値に置き換え)→ クリーニング済みの画像に標準の K-means 量子化を実行 → 輪郭を抽出 → <path> で塗りつぶします。
合成:塗りつぶしは、レイヤー化された <g> グループ内でストロークの背後に描画されます。
# Auto-detect: classifies input and routes automatically
svg, flow = image_to_svg("kandinsky.jpg", mode="graphic")
# Force compositional pipeline
svg, flow = image_to_svg("technical_drawing.png", mode="graphic", pipeline="compositional")
# Force fill-only (previous default behavior)
svg, flow = image_to_svg("photo.jpg", mode="painting", pipeline="fill")
パイプラインの選択(pipeline パラメーター):
| 値 | 動作 |
|---|---|
"auto"(デフォルト) | エッジ密度、輝度の二峰性、Hough 線の数に基づいて入力を分類します。グラフィックアートにはコンポジションパイプライン、写真には塗りつぶしのみのパイプラインを適用します。 |
"fill" | 塗りつぶしのみのパイプラインを強制します。写真、絵画、またはコンポジションパイプラインで望ましくない結果が出る場合に使用します。 |
"compositional" | 2 パスのパイプラインを強制します。線画、技術図面、インク画など、線が含まれると分かっている場合に使用します。 |
自動分類のヒューリスティック:エッジ密度が高く(エッジピクセルが 5% 超)、輝度分布が二峰性(双峰性係数が 0.35 超)の画像、または直線密度が高い画像(1 万ピクセルあたりの Hough 線が 3 本超)は、グラフィックとして分類されます。
SVG の出力構造(コンポジション):
<svg ...>
<rect ... />
<g id="fills" stroke-width="1" stroke-linejoin="round">
<path class="c3" d="m12 40l5-2..z"/>
</g>
<g id="strokes">
<line x1="..." y1="..." x2="..." y2="..." stroke="#000" stroke-width="2.5" stroke-linecap="round"/>
</g>
</svg>
ストローク幅の制御:検出された各線に対して垂直に幅を測定し、その後 0.65 倍にスケーリングして、4.5 SVG 単位を上限とします。これにより、細い線の鮮明さを保ちながら、太い特徴が過度に膨らんだストロークとして描画されるのを防ぎます。
現在の制限 — 直線のみ:Hough 変換で検出できるのは直線のセグメントです。曲線のストローク(円弧、らせん)はまだストロークとして抽出されず、塗りつぶしのパスに回されます。今後の対応として、スケルトンの分岐に対する cv2.fitEllipse またはスプラインフィッティングを予定しています。
パレットの再マッピング(ウォーホル風エフェクト)
構造と色を分離します。K-meansで領域を検出し、パレットの再マッピングで大胆な色を割り当てます。これにより、シルクスクリーン/ポップアート風のエフェクトが得られます。
# Named preset
svg, flow = image_to_svg("photo.jpg", mode="graphic", K=4, palette="pop")
# Custom hex list (darkest → lightest mapping order)
svg, flow = image_to_svg("photo.jpg", mode="graphic", K=8,
palette=["#000", "#dc143c", "#ff69b4", "#ffd700", "#32cd32", "#00bfff", "#ff8c00", "#f5f5f5"])
# Override background separately
svg, flow = image_to_svg("photo.jpg", mode="graphic", K=4, palette="ocean", bg_color="#000000")
組み込みプリセット:bw、mono3、mono4、pop、pop2、neon、warhol4、warhol6、warhol8、sunset、ocean
仕組み:形状の色を重複なく抽出し、輝度順に並べます。パレットの各要素は比例してマッピングされ、最も暗いクラスターは palette[0] に、最も明るいクラスターは palette[-1] に置き換えられます。bg_color が設定されていない場合、背景にはデフォルトで最も明るいパレット要素が使われます。パレットの長さをKに合わせる必要はありません。色は比例して区分されます。
ポートレート:大胆なパレットを使う場合でも、K=16〜24にします。顔の特徴(メガネ、ひげ、眉)には、Kが小さいと失われる階調の幅が必要です。目安として、輝度をきれいに区分するには、パレットの長さをK/3程度にします。K=8で4色のパレットを使うと、顔は特徴のない塊になります。
コントラストの前処理に関する警告:外部でコントラストを強調(contrast-stretch、sigmoidal-contrast)すると、背景検出に支障が出ることがあります。パイプラインのエッジ接触ヒューリスティックは、加工前の輝度分布を前提としています。強いトーンマッピングによって被写体の階調が背景に近いビンに押し込まれると、誤分類が起こります(たとえば、暗いジャケットの領域が背景と判定され、最も明るいパレット色に置き換えられるなど)。被写体の領域が背景色に崩れてしまう場合は、まず前処理をせずに試してください。パイプライン内蔵のバイラテラルブラーと、オプションのkuwahara/oilpaintで階調を分離できます。
背景検出のオーバーライド(bg_clusters)
背景として扱うクラスターを指定できます。
# Auto-detect (default) — edge-contact heuristic
svg, flow = image_to_svg("photo.jpg", mode="illustration", K=20, palette="warhol6")
# Disable — no clusters removed, no background rect color override
svg, flow = image_to_svg("photo.jpg", mode="illustration", K=20, palette="warhol6", bg_clusters=0)
# Force specific cluster indices (from quantize step's sorted_clusters output)
svg, flow = image_to_svg("photo.jpg", mode="illustration", K=20, palette="warhol6", bg_clusters=[2, 5])
パレットの再マッピングですでにすべての色を明示的に制御しており、背景検出が妨げになっている場合は、bg_clusters=0 を使います。どのクラスターが背景か分かっているものの、ヒューリスティックが誤って判定する場合は、bg_clusters=[list] を使います。
ポートレートのポップアート風レシピ(ウォーホルスタイル)
# Key: enough K for facial features, palette length ~K/3, modest smoothing
# Do NOT apply contrast preprocessing — it breaks background detection.
results = image_to_svg_batch("portrait.jpg", [
{"name": "hot", "mode": "illustration", "K": 20, "smooth": "kuwahara:6",
"palette": ["#000", "#D4145A", "#FF6B9D", "#FF85C0", "#FFD700", "#FFEF82", "#FFF8DC"]},
{"name": "cool", "mode": "illustration", "K": 20, "smooth": "kuwahara:6",
"palette": ["#0D0035", "#4A00E0", "#7B68EE", "#00D4FF", "#7FFFD4", "#B0FFE0", "#E0FFFF"]},
{"name": "earth", "mode": "illustration", "K": 20, "smooth": "kuwahara:6",
"palette": ["#1a0a00", "#8B4513", "#CD853F", "#DEB887", "#F5DEB3", "#FAEBD7", "#FFF8DC"]},
{"name": "neon", "mode": "illustration", "K": 20, "smooth": "kuwahara:6",
"palette": ["#0d0d0d", "#ff00ff", "#00ff00", "#ffff00", "#00ffff", "#ff69b4", "#f5f5f5"]},
], svg_width=700)
この設定が効果的な理由:K=20なら、顔の構造(メガネ、ひげ、眉の隆起)を保つのに十分な階調クラスターが得られます。7色のパレットでは、輝度ビンがおよそK/3となり、色が濁ることなく特徴を分離するのに十分な変化が得られます。kuwahara:6 はエッジを消さずにテクスチャを滑らかにします(:12 ではメガネが消えます)。未加工のソース画像を使い、パイプライン内の平滑化のみを適用します。外部でのコントラスト調整は行いません。
ImageMagickによる前処理(smooth)
量子化の前に、ImageMagickのエッジ保持フィルターを使って形状数とSVGファイルサイズを20〜45%削減します。PATH上にImageMagickが必要です(Claude.aiのコンテナにはプリインストールされています)。
# Oilpaint: bold, painterly smoothing (default strength=8)
svg, flow = image_to_svg("photo.jpg", mode="photo", smooth="oilpaint")
# Stronger smoothing = fewer shapes, more stylized
svg, flow = image_to_svg("photo.jpg", mode="illustration", K=32, smooth="oilpaint:12")
# Kuwahara: subtler, preserves more structure (default strength=5)
svg, flow = image_to_svg("photo.jpg", mode="painting", smooth="kuwahara:7")
# Works with batch API too
results = image_to_svg_batch("photo.jpg", [
{"name": "raw", "mode": "photo"},
{"name": "smooth", "mode": "photo", "smooth": "oilpaint"},
{"name": "stylized", "mode": "illustration", "K": 32, "smooth": "oilpaint:12", "palette": "pop"},
])
使用可能なフィルター:oilpaint(ImageMagickの -paint)、kuwahara(ImageMagickの -kuwahara)。強度をカスタマイズするには、:N を追加します。
仕組み:IMフィルターは、パイプラインのバイラテラル+ガウシアンブラーの前に実行されます。どちらも異なるスケールでエッジを保持する平滑化処理です。IMは粗いテクスチャを処理し、バイラテラルブラーは細部を処理します。その結果、K-meansで断片化した形状が減り、領域がよりクリーンになります。
測定結果(1206×1597の写真、K=32):
| smooth | 形状数 | SVGサイズ | 削減率 |
|---|---|---|---|
| なし | 3381 | 1868KB | — |
| oilpaint (8) | 2385 | 1329KB | -29% |
| oilpaint:12 | 1842 | 1065KB | -43% |
| kuwahara (5) | 2719 | 1453KB | -22% |
| kuwahara:7 | 2000 | 1152KB | -38% |
パイプラインのアーキテクチャ
flowing DAGランナーを使用します。入力が独立しているステップは並列に実行されます。
塗りつぶしのみのパイプライン(pipeline="fill")
preprocess → quantize → ┬─ detect_background ─┬─ extract_contours → assemble_svg
└─ edge_map ─┘
合成パイプライン(pipeline="compositional")
classify_input ──→ extract_lines ──→ suppress_line_regions ──→ [fill pipeline on cleaned image]
│ │
└──────────── lines ───────────────────→ assemble_compositional ←── fills
ステップ(塗りつぶしのみ):
- preprocess — バイラテラルフィルターとガウシアンぼかし(エッジを保持しながらテクスチャを除去)
- quantize — 指定した K で K-means による色の量子化
- detect_background — エッジへの接触に基づいて背景クラスターを特定(edge_map と並行して実行)
- edge_map —
cv2.Sobelによる Sobel エッジ検出(detect_background と並行して実行) - extract_contours — 暗い領域を考慮し、木版画風になるのを防ぎながらクラスターごとに輪郭を抽出(d=1 の膨張。ストロークで隙間を処理)
- assemble_svg — ストロークと塗りつぶしの隙間をカバーする、Z 順のペインターアルゴリズムによる組み立て
追加ステップ(合成):
- classify_input — エッジ密度、二峰性、ハフ直線の本数を分析
- extract_lines — モルフォロジー処理による細い特徴の分離 → 細線化 → ハフ変換 → 共線の線を結合 → ストローク幅を測定 → 色をサンプリング
- suppress_line_regions — 線のピクセルを、メディアンぼかしで推定した背景に置き換え
- assemble_compositional — グループ化した SVG 内で、ストロークの背後に塗りつぶしレイヤーを配置
失敗時の再開
svg, flow = image_to_svg("source.jpg", mode="photo")
# If extract_contours failed:
flow.override(extract_contours, corrected_shapes)
flow.resume() # quantize, detect_background, edge_map stay cached
バッチ API
1 枚の画像から複数のバリアントを生成します。同じ K の実行間では計算を共有します。
from pipeline import image_to_svg_batch
results = image_to_svg_batch("photo.jpg", [
{"name": "photo", "mode": "photo"},
{"name": "warhol", "mode": "graphic", "K": 12, "palette": "warhol4"},
{"name": "neon", "mode": "graphic", "K": 12, "palette": "neon"},
{"name": "sunset", "mode": "graphic", "K": 12, "palette": "sunset"},
{"name": "bw", "mode": "graphic", "K": 16, "palette": "bw"},
], svg_width=1400)
for name, svg in results.items():
with open(f"{name}.svg", "w") as f:
f.write(svg)
同じ K を共有するバリアントでは、パイプライン(preprocess → quantize → edge_map → extract_contours)を一度だけ実行し、その後、組み立て時にパレットの再マッピングへと分岐します。これにより、パレットのバリアント間で構造の同一性(同じ形状、同じパス)が保証され、同じ K のグループごとに約 20~60 秒を節約できます。
バッチモードでも検証は必要です。 バッチ処理は手軽なため、並べて比較する手順を省略しがちですが、必ず行ってください。K のグループごとに少なくとも 1 つのバリアントをレンダリングし、納品前に検証してください。背景検出の失敗やパレットのマッピングの問題は、レンダリングしないと見つかりません。
検証プロトコル
実行するたびに、レンダリングして並べて目視比較してください。 これは必須です。
import subprocess
from PIL import Image
subprocess.run(['rsvg-convert', '-w', '1400', 'output.svg', '-o', 'output.png'])
orig = Image.open('source.jpg')
rendered = Image.open('output.png')
target_h = 800
orig_r = orig.resize((int(orig.width * target_h / orig.height), target_h))
rend_r = rendered.resize((int(rendered.width * target_h / rendered.height), target_h))
gap = 20
comp = Image.new('RGB', (orig_r.width + rend_r.width + gap, target_h), (255,255,255))
comp.paste(orig_r, (0, 0))
comp.paste(rend_r, (orig_r.width + gap, 0))
comp.save('comparison.png')
# LOOK AT comparison.png BEFORE claiming success
手動での後処理
微妙な色の違いへの対処
輝度が似ていても色相や彩度が異なる 2 つの領域は、RGB 空間での K-means では同じ色にまとめられます。HSV のマルチスペクトル分析を使用してください。
hsv = cv2.cvtColor(rgb, cv2.COLOR_RGB2HSV)
h_ch, s_ch, v_ch = hsv[:,:,0], hsv[:,:,1], hsv[:,:,2]
# Separate gray (low saturation) from red (high saturation) at similar brightness
red_mask = ((h_ch < 12) | (h_ch > 168)) & (s_ch > 120) & (v_ch > 80)
gray_mask = (s_ch < 80) & (v_ch > 40) & (v_ch < 120) & spatial_constraint
グレースケールでは似て見えても、見た目には異なる色を見分けるうえで、彩度が重要な識別要素です。
オーバーレイの配置
量子化で抽出されなかった図形を追加する場合は、元画像ではなく SVG のレンダリングから座標を導き出してください。抽出パイプラインでは、輪郭の簡略化によって位置がずれるためです。
# WRONG: extract from source, insert into SVG (coordinate mismatch)
# RIGHT: render SVG → detect gap in render → create shape in render coords → insert
svg_render = cv2.imread('rendered_svg.png')
出力形式
v1.9.0 以降、塗りつぶしはコンパクトな形式で書き込まれます。Starry Night では、描画出力のサイズが 3.64 MB から 1.35 MB(gzip 圧縮時は 825 KB から 293 KB)になり、Chromium と librsvg のどちらでも同じようにレンダリングされます。
<svg xmlns="http://www.w3.org/2000/svg" id="s1a2b3c" viewBox="0 0 1000 625">
<style>#s1a2b3c .c0{fill:#3e85bb;stroke:#3e85bb}#s1a2b3c .c1{...}</style>
<rect width="1000" height="625" fill="#16151e"/>
<g stroke-width="1" stroke-linejoin="round">
<path class="c0" d="m793 0l-10.8 2.9-1 14.7..z"/>
</g>
</svg>
- パレットの各色に CSS クラスを 1 つ割り当て、ドキュメントごとの ID の下にスコープを設定します。これにより、複数の SVG を 1 つのページにインライン化してもクラスが衝突しません。
- 同じ色が連続する図形は 1 つの
<path>に統合されます(描画順は維持され、evenodd の図形は個別のままです)。そのため、要素数は図形数より少なくなります。 - パスデータには、
10^-precision単位のグリッド上で相対m/lコマンドを使用します。precision=1(デフォルト)は輪郭のグリッドに一致し、情報の損失はありません。precision=0は viewBox の整数単位に丸められ、平均ピクセル差が約 1%で、サイズを約 20%削減します。 - 図形は内部では絶対座標の
"M x,y L x,y Z"形式のパス文字列を保持します(flow.value(extract_contours)["shapes"])。コンパクトになるのは出力された SVG のみです。 - 色をアニメーションさせるには、そのクラスを指定します:
@keyframes k{50%{fill:#f0f;stroke:#f0f}} #s1a2b3c .c0{animation:k 4s infinite}。
ギャップのカバー:stroke=fill
すべての塗りつぶし <path> は、独自の塗りつぶし色で、stroke-width="{gap_stroke}" を指定してストロークされます。角はラウンド結合です(塗りつぶしグループに一度だけ設定します)。これにより、各シェイプが独自の塗りつぶし色で外側に広がり、クラスタ間のギャップをその場所に適した色でカバーします。
自動スケーリング:gap_stroke は max(1.0, round(svg_width / source_width)) として計算されます。svg_width=1000 の場合、幅 500px のソースでは gap_stroke=2、幅 1000px のソースでは gap_stroke=1 になります。これにより、シェイプ数の多い SVG を縮小表示した際に、ストロークのにじみが過剰になって明るいハローが見える「snow」アーティファクトを防ぎます。明示的に指定して上書きできます:image_to_svg("img.jpg", gap_stroke=3)。
ギャップのカバーにおいて膨張処理よりストロークが優れている理由:膨張処理は輪郭を単純化する前に二値マスクに対して行われ、細部をぼかします。ストロークは approxPolyDP の後に最終ポリゴンに適用されるため、単純化によって生じたものも含め、すべてのギャップをカバーします。純粋なベクター処理です。v1.9.0 以降、ストロークのコストは各色のクラスにおける stroke 宣言のみです。
背景のフォールバック:detect_background がクラスタを検出しない場合、背景の矩形には白ではなく #000000(黒)を使用します。黒は影として認識されますが、白は何もない状態として認識されます。
膨張処理は iterations=1 に減らします。形態学的ノイズの除去に必要な最小限の処理です。ギャップのカバーはすべてストロークで処理します。
アンチパターン
- 見た目の解釈に基づいて、シェイプを手描きしないでください。CV 抽出を使用してください。
- レンダリングして比較せずに、修正が機能すると主張しないでください。レンダリングした比較だけが検証になります。
- 抽出した輪郭を近似するために、幾何学的プリミティブ(円、矩形)を使用しないでください。
- アラインメントを確認せずに、ソース画像から座標を抽出して SVG に挿入しないでください。
- 全体の彩度を上げないでください。測定した ΔE に基づいて、色ごとに対象を絞って調整してください。
- 背景に近い色を積極的に統合しないでください。背景との RGB 距離が 10 未満で、かつエッジで強く接している色のみを統合してください。
- リクエストがない限り、ベジェ曲線による平滑化を使用しないでください。シンプルな L 字型ポリゴンのほうが SVG のサイズを小さくできます。
- 3×3 より大きな膨張カーネルを使用しないでください。3×3 カーネルで
iterations=1を使用してください。stroke=fill がベクター空間でギャップをカバーするため、膨張処理はノイズによる穴を埋めるだけで十分です。
既知の制限事項
- 細い線画(塗りつぶしのみ):写真で版画のようなアーティファクトを防ぐ暗いシェイプのゲーティングにより、グラフィックアートの意図的な細線が除外されることがあります。
"graphic"モードでは条件が緩和されますが、非常に細かいクロスハッチングは劣化する場合があります。線画の入力にはpipeline="compositional"を使用してください。細い特徴が SVG のストロークとして抽出されます。 - 曲線(コンポジショナル):ハフ変換で検出できるのは直線分のみです。曲線のストローク(弧、らせん、フリーハンドの曲線)は塗りつぶし処理に回され、塗りつぶされたポリゴンとしてレンダリングされます。今後の対応:
cv2.fitEllipseまたはスケルトンの分岐に対するスプラインフィッティング。 - リング/弧の構造:大きな暗色のリング(Kandinsky の外側の円など)は、複数の K-means クラスタに分断されます。各クラスタの輪郭は独立しているため、リング全体が滑らかな一つのシェイプになりません。暗色クラスタを統合する処理があれば改善できます。
- グラデーションの遷移:K の値にかかわらず、滑らかなグラデーションでは階段状のバンディングが発生します。K を大きくすると軽減されますが、完全には解消されません。
- 平行線のグループ(コンポジショナル):密なハッチングや罫線は、隣接する線間の垂直距離が統合のしきい値(6px)未満の場合、誤って統合されることがあります。現在の統合処理では、平行かつオフセットされた線を別々のストロークとして検出できません。
依存関係
pip install opencv-python-headless scikit-image scipy scikit-learn --break-system-packages
apt-get install -y librsvg2-bin # for rsvg-convert
コンパイルによる高速化:gcc が利用可能な場合、nn_assign.c は初回使用時に自動コンパイルされます(ラベル割り当てが 27 倍高速になります)。利用できない場合は numpy にフォールバックします。
スキル間の依存関係(pipeline.py によって自動解決されます):
- flowing — DAG ワークフローランナー