ImageMagick 使用例 -- 形状のモルフォロジー
- モルフォロジー演算子
- 基本の組み込みモルフォロジーカーネル
- モルフォロジー演算の反復(繰り返し)
- 変化の詳細出力 (監視用)
- 生成されたカーネルの表示 (デバッグ用)
- カーネルの画像生成 (デバッグ用)
- 平坦なグレースケールモルフォロジー
- 真のグレースケール(3次元)モルフォロジー
- 基本手法の輝度版
- 形状集合の粒度(グラニュラリティ)
-
非対称カーネルの効果 (手法のテスト)
- Thicken - マッチするピクセルを追加
- Thinning - マッチするピクセルを除去
- アンチエイリアス形状での距離
-
形状からのスケルトン生成 作成中
- Autotraceによるスケルトン
モルフォロジーは、各ピクセルを取り巻く近隣の他ピクセルの「近傍」に基づいて、さまざまな形で画像を変更します。これにより、形状の拡大・縮小(膨張/収縮)から、エッジからの距離、スケルトン(中心軸)までの細線化に至るまで、膨大な範囲の効果が得られます。次のセクションで扱うぼかしや鮮鋭化を提供するより古典的な「畳み込み(convolution)」技法でさえ、ある意味モルフォロジー手法の一種です。本質的にモルフォロジーとは、画像内に見出されるオブジェクトの形状を、変更・判定・発見するためのものです。
モルフォロジー入門
モルフォロジーはもともと、画像内の形状の構造を整理し研究するための手法として開発されました。これは、画像内の各ピクセルをその近隣とさまざまな方法で比較し、そのピクセルを追加・除去したり、明るく・暗くしたりすることで機能します。画像全体に、場合によっては反復的に適用することで、特定の形状を見つけたり、除去・変更したりできます。例えば、あるピクセルが白で、その周囲が完全に他の白ピクセルで囲まれているなら、そのピクセルは明らかに画像の縁(エッジ)にはありません。そこでそのピクセルを黒にして、エッジのピクセルだけを「オン」のまま残すことができます。これは '[EdgeIn](#edgein)'(後述)として知られる手法です。この処理全体は実際には「構造要素(Structuring Element)」あるいは「カーネル(Kernel)」の定義に依存しており、これが各モルフォロジー手法において「近隣」と分類されるピクセルを定義します。この「近傍」のサイズや形状をどうするかは、何を達成しようとしているのか、あるいは画像内で具体的に何を探しているのかに依ることがよくあります。以下に、さまざまなカーネルを画像に変換したいくつかの例を示します(特殊なスクリプト "[kernel2image"](../static/img/scripts/kernel2image)" を使用)。中央のピクセル「原点(origin)」を取り巻く「近傍」のいくつかを表しています。
これらの画像は、カーネルの個々の要素を強調するために拡大されています。ご覧の通り、典型的なカーネルはしばしば非常に小さいものです。実際、上で示した 'Disk' カーネルは本当は "
" であり、それが上で示したなかでも最大級のカーネルの一つです。しかし「カーネル」は本当は画像ではありません。それは単に浮動小数点値の配列であり、そのうちの一要素がカーネルの「原点」として指定されています。この特別な要素は、定義された近傍によって「影響を受ける」ピクセルの位置であり、必ずしもそうとは限りませんが、典型的には対称的なカーネルの中央ピクセルです。なお、これらは可能な近傍のほんの一例にすぎません。一部のカーネルは、そのカーネル固有の '半径(radius)' 引数を増やすことで、典型的にはより大きくできますが、特殊用途に使われる他のものは固定サイズです。最初の2つのような単純なカーネルの場合、モルフォロジー手法を繰り返す(反復する)ことで、カーネルの実効「サイズ」を増やし、(マークされた)「原点」からより遠くのピクセルにまで影響を及ぼすことができます。ただしこれは必ずしもうまくいくわけではなく、予期しない結果を生むこともありますが、より大きなカーネルを直接使うより速い場合もあります。とはいえ、それも常にそうとは限りません。モルフォロジーの研究論文で「SE」(構造要素)と呼ばれるカーネルとしての「構造要素」の最終的なサイズと形状は、この形状より大きい、または小さい画像要素を位置特定して強調・削除する手段として重要です。これこそが、画像内のさまざまな要素を選り分ける手段としてモルフォロジーを極めて強力にしているものです。しかし、カーネルが大きいほどモルフォロジー手法に要する時間は長くなるので、カーネルは小さく保つほうが望ましいです。示したカーネルはすべて、最後の一つを除いて実際に「形状を持って」います。透明な部分はカーネルの定義された「近傍」の 一部ではありません。つまり、それらは有効な値を持たず、どのモルフォロジー計算にも参加しません。最後から2番目のカーネル 'Corner #0' が、「オン」の値だけでなく、その「形状」の一部として「オフ」の値も持っていることに注目してください。これらの値は、透明な値(形状の一部でない)と同様に、ヒットアンドミスや関連手法(後述)にとって重要です。この特定のカーネルは、画像内の二値形状の「コーナー」ピクセルを位置特定するための一連のカーネルの最初の一つにすぎません。上で示した最後の「カーネル」は、大きな矩形(正方形)領域全体にわたって完全に定義されています。また、他のカーネルが 1(白)、0(黒)、あるいは特殊な「未定義」値のみを使うのに対し、このカーネルの値は実際には縁付近のほぼゼロ(ほぼ黒)から中央の最大(純白)値まで及びます。さらに、こうしたカーネルは負の値や、他のカーネルの通常の範囲をはるかに超える非常に大きな値さえ使うことができます。カーネルは本当に単なる値の配列にすぎず、これらは 0 から 1 の範囲だけでなく、どんな値でも取りうることを思い出してください。この種のカーネルは、モルフォロジーそのものよりずっと長く存在してきた特殊手法である「畳み込み演算」において特に重要です。その結果、IM にはこの種の組み込み(「名前付き」)カーネルが非常に多く用意されています。これは次の IM 例のセクション「画像の畳み込み」でより詳しく扱います。さて、すでに述べたように、カーネルは本当は画像ではありません。それは単に浮動小数点値の配列にすぎません。これらの実際の値(上では閲覧のために画像に変換しました)は後で見ていきます。
モルフォロジー演算子
"[-morphology](https://imagemagick.org/command-line-options/#morphology)" 演算子は、ユーザーにその動作に対する多くの制御を提供するため、非常に複雑です。
-morphology {_method_}[:{_iterations_}] {_kernel_}[:[_k_args_}]
なお、少なくとも2つの項目を指定する必要があります。一つはモルフォロジーの 'method(手法)'で、画像に適用したい演算の種類を演算子に伝えます。もう一つは 'kernel(カーネル)' で、どの「近隣」ピクセルが最終結果に影響するかを指定します。どちらも等しく重要であり、どちらも広範な影響を及ぼしえます。利用可能な手法の一覧は "-list morphology " で取得できます。IM に含めた組み込みカーネルの一覧は "-list kernel " で見られます。さまざまな手法、およびそれらの手法が使うカーネルについては後で順に説明します。 | _"[-morphology](https://imagemagick.org/command-line-options/#morphology)" 演算子(基本手法)と最初のカーネルセットは、私が中国で休暇中に ImageMagick バージョン 6.5.9-0 に追加しました。2009年12月から2010年1月にかけてです。
ただし、より古く密接に関連する "[-convolve](https://imagemagick.org/command-line-options/#convolve)" 手法を使って、単純化された「正方形」カーネルのモルフォロジーを実行することは可能です。下記の 代替的な基本モルフォロジー技法 を参照してください。
基本の組み込み形状カーネル
カーネルはすべてのモルフォロジー手法に共通であり、各種手法の結果は実際に選択したカーネルに大きく依存するため、まず使うカーネルをどう定義・選択するかを見ていきます。良い選択肢となるカーネルがすでにいくつも事前定義されており、しばしばそれ以上のものを探す必要はありません。事前定義された組み込みカーネルの一覧は "-list kernel" を使って取得できます。すべてのカーネルは特定のサイズを持ち、典型的には1辺が奇数ピクセルの正方形で、その中央がカーネルの「原点」です。ただし、後述のように "[-morphology](https://imagemagick.org/command-line-options/#morphology)" 演算子はこの制限に縛られません。組み込みカーネルで最もよく使われ、一般に最初に与える k_argument は 'radius(半径)' です。これは、カーネルの典型的な奇数サイズの正方形近傍をどれだけ大きくするかを定義します。最終的なカーネルサイズは一般に半径の2倍プラス1(中央ピクセル分)になります。つまり 'radius ' が '2' なら、5×5 ピクセル四方のカーネルが作られます。'radius ' は典型的には最終カーネルのサイズ、ひいては画像に対するモルフォロジー演算の全体的な速度を定義しますが、特に 畳み込みカーネル ではカーネルのサイズより値の方が結果に大きな影響を与えるため、最も重要な要因とは限りません。'radius ' が 0 に設定されるか未定義のままだと、'radius ' は関与するカーネルに応じて、何らかの妥当な、あるいは最もよく使われる値に自動的にデフォルト設定されます。
Unity
これは「No-Op(何もしない)」カーネルが必要なときに特別に使われる特殊なカーネルです。このカーネルを使うほとんどのモルフォロジー手法は、元の画像をそのまま再生するか、空白の結果を生成します。このカーネルに引数はありません。この全く同じ単一要素カーネルは '[Disk:0.5](#disk)' を使っても生成でき、これならカーネル生成の一部としてスケーリング引数を指定することもできます。
Diamond
最も最小限の、ただしおそらく最も単純とは言えないカーネルが 'Diamond ' 組み込みカーネルです。基本的なカーネルを見る簡単な方法は、黒背景に単一の白ピクセルを含む画像に対して Dilate(膨張)モルフォロジー手法を使うことです。これは基本的に、その単一ピクセルをカーネル近傍の「形状」へと拡張します。以下は、最小限の 'Diamond' 組み込みカーネルで 'Dilate' を使い、見やすくするために結果を拡大した例です。
magick xc: -bordercolor black -border 5x5 pixel.gif
magick pixel.gif -scale 800% pixel_mag.gif
magick pixel.gif -morphology Dilate Diamond \
-scale 800% k_diamond.gif
| この IM 例の領域におけるカーネル画像の結果はすべて、個々のピクセルを見られるように拡大してあります。実際には、ここで示しているカーネルや結果はすべて、本来あるべき通り非常に小さいものです。この場合、膨張される画像はサイズがわずか 11×11 ピクセルで、表示のために 8 倍に拡大されています。
---|---
これは実はモルフォロジー演算にとってかなり良いカーネルであり、基本的に最も最小限の実用的近傍を定義します。すなわち、元のピクセルに加え、それに直接接する4つのピクセルです。この種のカーネルの別名は 'Z4' 構造要素です。見た目は小さな「プラス」記号のようです。ダイヤモンド形は半径が増えるにつれて初めて明らかになります。このカーネルの省略可能な k_arg は、次のように2つの値を取れます…
Diamond[:{_radius_}[,{_scale_}]]
すべての形状カーネルにとって最も重要な引数は radius であり、前述の通り、これは中央の「原点」から最も近い縁までの距離を表す整数です。したがって最終的な 'Diamond' カーネルは、ダイヤモンド形を含む(半径の2倍プラス1の)正方形です。以下は、より大きな radius を使って大きなカーネルを生成した結果です。
for r in 1 2 3 4; do
magick pixel.gif -morphology Dilate Diamond:$r -scale 800% k_diamond:$r.gif
done
![[IM Output]](../static/img/morphology/k_diamond_1.gif)
Diamond:1
(デフォルト) | ![[IM Output]](../static/img/morphology/k_diamond_2.gif)
Diamond:2 | ![[IM Output]](../static/img/morphology/k_diamond_3.gif)
Diamond:3 | ![[IM Output]](../static/img/morphology/k_diamond_4.gif)
Diamond:4
---|---|---|---
もう一つの k_argument は scale で、デフォルト値は 1.0 です。典型的にはこれは、カーネルが形状を作るために実際に使う値を変更するために用いられます。これは一般に Convolve や グレースケールモルフォロジー のような特殊手法に対してのみ重要です。
Square
'Square ' は、他の代替的技法を使って最も適用しやすいため、モルフォロジーで最もよく使われるカーネルです。ただし、最も最小限のカーネルではありません(上記 '[Diamond](#diamond)' を参照)。 デフォルトでは 'Square' カーネルは「中央」の周囲の 3x3 ピクセル近傍を使います。 |
magick pixel.gif -morphology Dilate Square -scale 800% k_square.gif
![[IM Output]](../static/img/morphology/k_square.gif)
基本的にこれは、元のピクセルの8つの近隣すべてが、そのピクセルの近傍の一部として分類されることを意味します。その結果、ピクセルを平均化したり、ある形状を1ピクセル分拡大・縮小したりするのに良いカーネルです。すべての形状カーネルと同様、上の Diamond カーネル で示したのと同じ k_arguments を取り、最初の引数 radius が最も重要です。
for r in 1 2 3 4; do
magick pixel.gif -morphology Dilate Square:$r -scale 800% k_square:$r.gif
done
![[IM Output]](../static/img/morphology/k_square_1.gif)
Square:1
(デフォルト) | ![[IM Output]](../static/img/morphology/k_square_2.gif)
Square:2 | ![[IM Output]](../static/img/morphology/k_square_3.gif)
Square:3 | ![[IM Output]](../static/img/morphology/k_square_4.gif)
Square:4
---|---|---|---
このカーネルのデフォルト(radius=1)は前述の通り 3×3 の正方形で、(関与する直近の近隣の数から)一般に 'Z8' 構造要素として知られています。
Octagon
'Octagon ' カーネルは8辺の形状のカーネルです。そして '[八角形距離メトリック](../static/img/morphology/octagonal)' に一致するよう特別に設計されました。両者は全く異なるカーネルなので混同しないでください。以下は小さな半径に対する結果のカーネルです…
半径 1 では "Diamond" カーネルと同じカーネルになることに注意してください。このため、八角形のデフォルトサイズは半径 '2' です。 | _この時点以降、私はカーネルの画像生成に特殊な kernel2image スクリプトを使います。(上のような)生の「dilate-scale」法を使うよりずっと明瞭だからです。ただし、カーネルは一般に非常に小さいことを思い出してください。とはいえ Octagon や Disk カーネル(次を参照)は、特定の用途に対して非常に大きくなることがあります。
---|---
| "Octagon" カーネルは "[Octagonal](#octagonal)" 距離カーネルとともに IM v6.6.9-4 で追加されました。_
---|---
Disk
'Disk' カーネルは、ご想像の通り円形の形状です。そして非常に大きなモルフォロジーカーネルが必要なときによく使われます。ただし、これはエイリアスのかかった真偽値(boolean)の円であることに注意してください。とはいえ、ディスクの radius 引数は浮動小数点数にでき、小さな半径を使ってかなり多様な形状を作れます。
'Disk:4.3' カーネルがデフォルトであり、私が最初の真のディスク形状とみなすものです。このサイズ以上のディスクは、画像の形状を全般的に丸めて滑らかにするのに特に適しています。ディスクを含むカーネルの最終的なサイズは、'radius ' 値を切り捨てて2倍プラス1したものです。したがってデフォルトの 'Disk:4.3' カーネルはカーネルサイズの半径が 4 で、最終的なカーネルサイズは 4 の2倍プラス1、つまりディスク形状を保持する 9×9 のカーネルを生成します。1 未満(ただしゼロでない)の値は常に単一ピクセルのカーネルを生成しますが、それはあまり有用ではありません。その後カーネルは、主として前述のカーネル種別でも生成できるカーネルを生成する傾向にあります。半径が大きくなって初めて、真のディスク形状のカーネルが現れ始めます。最も注意すべき点は、整数の半径を使うより、小数の半径を使うほうがはるかにうまく働くということです。ディスクの側面に妙に見える単一ピクセルが生じるのを避けるため、おおよそ 0.3 から 0.5 程度の端数を加えることが一般に推奨されます。
Plus
'Plus' カーネルは、ピクセル周囲の単純な「近傍」ではなく、特定の「形状」を表すよう設計されている点で、他のモルフォロジー形状カーネルとは少し異なります。このカーネルでより大きな 'radius ' を使っても、単にカーネルのサイズが増えるのではなく、結果として得られるプラス記号の腕が長くなります。ただし、腕の太さは増えません。
'Plus' カーネルのデフォルトサイズは半径 2 で、中央の「原点」の周囲に 2 ピクセルの「腕」を生成します。'Plus:1' カーネルはたまたまデフォルトの 'Diamond' カーネルと同じになります。'Plus' カーネルは一般に通常のモルフォロジー手法には使われず、そうした用途には避けるべきであることに注意してください。しかし、後で スケルトン情報 を表示するときに私が行うように、画像内の単一の点を見つけて強調したい場合には非常に有用です。基本的にこれは、画像内で個々の「点」がどこにあるかを正確に知る必要なく、シンボルの描画 を行う手段を提供します。
Cross
'Cross' カーネルは '[Plus](#plus)' と全く同じですが、45度回転しています。これもまた、各種の点の位置を示すためにピクセルを拡張するのに適した特殊なカーネル形状にすぎません。
Ring
'Ring' カーネルは '[Plus](#plus)' カーネルと同様、ピクセルをマークしたり画像上にパターンを生成したりするための特殊な「形状」カーネルとして設計されています。ただし、半径を1つだけ取るのではなく、2つの半径を取ることができ、Disk カーネル と同じように定義されます…
Ring[:{_radius1_}[,{_radius2_}[,{_scale_}]]]
これが行うのは、与えられた2つの半径の順序にかかわらず、2つの半径の間に入るピクセルを「オン」にすることです。半径が与えられなければ、'2.5' と '3.5' の半径にデフォルト設定され、'Ring:2.5,3.5' が生成されます。これは中空の八角形リングのように見え、ピクセルを丸く囲むのに理想的です。2つの半径を変えることで、任意のサイズと太さの「リング」を作れます。半径への小さな変更は、縁の周りにごく少数のピクセルを足し引きするので、リングの見た目を細かく制御できます。2つの半径が互いに1ピクセル以内であれば、まばらに離れた点からなるリングも生成でき、これは特殊用途の近傍として有用なことがあります。小さな半径はまた箱状のカーネルを生成し、これも有用なことがあります。2番目の半径が与えられない場合、デフォルト値の '0.5' になり、これは事実上、完全なディスクを定義しますが、中央の「原点」ピクセルを除いたものになります。言い換えれば、ディスクカーネルだが「原点」ピクセルを除外したものです。以下は生成できる 'Ring' カーネルの多くの例です…
ご覧の通り、2つの半径を注意深く調整することで多くの可能性が得られ、画像内で注目すべき位置を示す良い方法になります。
Rectangle
'Rectangle' カーネルは上の '[Square](#k_square)' カーネルと密接に関連しており、デフォルトでは同じ正方形の 3x3 カーネルを生成します。ただし、単純な半径引数ではなく、'geometry(ジオメトリ)' 引数を与えて、望む矩形カーネルの正確なサイズを指定できます。以下にいくつかの指定と、それらが生成するカーネルの画像を示します。
デフォルトでは、カーネルは近傍の「原点」をカーネルの正確な「中央」に設定しようとします。ただし偶数サイズの矩形の場合は、必要に応じて中央のすぐ上および/または左の点を選びます。しかし、中央からずれた原点も指定できます。この特定のカーネルは、長い水平線や垂直線を定義するのにも適しており、画像内でそうしたオブジェクトを探すことができます。これについては後で詳しく述べます。現時点では、矩形に対して scale 係数を与えることはできません。すべてのカーネル値は 1.0 のみに設定されます。
ユーザー定義の自作カーネル
組み込みカーネルだけに縛られるのではなく、独自のカーネルを指定して、カーネルに使わせたい正確な値を与えることもできます…
"[{_geometry_}:] {_value_}, {_value_}, {_value_},....."
'geometry ' の指定は、基本的に前の '[Rectangle](#k_rectangle)' カーネルの引数とまったく同じです。これはカーネルのサイズを与え、省略可能で近傍の「原点」の「オフセット」を与えます。数値が1つだけ与えられた場合、それは正方形カーネルの寸法とみなされます。 |
geometry の値は「半径」引数ではなく、カーネル全体のサイズであることを思い出してください。 |
|---|---|
'geometry ' や ':' が指定されない場合、「旧」スタイルの指定を使っていることになります。与えられたすべての値を保持できる十分な大きさの、奇数サイズの正方形カーネルが生成されます。これは推奨されず、古いバージョンの ImageMagick との後方互換性のためにのみ提供されています。':'(これは geometry 指定の後に必須です)の後には、カンマおよび/または空白で区切られた width × height 個の浮動小数点値を与えます。特殊な値 'NaN'(「数でない(Not a Number)」の意味)または単独の '-' を使うと、カーネルのその点がモルフォロジー近傍の一部ではないことを指定できます。 例えば、以下は幅 3 の正方形カーネルの指定で、単一ピクセル画像の畳み込みぼかしに使えます。 |
magick pixel.gif -morphology Convolve \
"3: 0.3,0.6,0.3 0.6,1.0,0.6 0.3,0.6,0.3" \
-scale 800% k_user_3.gif
![[IM Output]](../static/img/morphology/k_user_3.gif)
単一ピクセルに対しては、Convolve は Dilate とほぼ同じように動作しますが、Convolve はカーネルの値を使い、近隣の値を拡張して足し合わせます。一方 Dilate は一般にオン/オフ(真偽値)の形状と、すべての近隣の最大値を使って動作します。ただし、孤立した単一ピクセルに真偽値形状を適用した場合は同じ結果になります。矩形カーネル定義の個々の行を区切るために、入力文字列に追加の空白(あるいは改行さえ)を加えられることに注目してください。 そしてここでは 5×3 の矩形領域を定義しましたが、特殊な 'nan'(数でない)値を使って角を切り落とし、楕円形のカーネルを作りました… |
magick pixel.gif -morphology Dilate \
"5x3: nan,1,1,1,nan 1,1,1,1,1 nan,1,1,1,nan " \
-scale 800% k_user_5x3.gif
![[IM Output]](../static/img/morphology/k_user_5x3.gif)
そして最後に、「原点」の周りに 'L' 字形を形成する矩形近傍を指定する例を示します。カーネルの一部でない部分を指定するために 'nan' の代わりに '-' を使いました。このカーネルの原点はそれ自身の近傍の一部ですらなく、カーネルの矩形境界内のどこにでも置けることに注意してください。 |
magick pixel.gif -morphology Dilate \
"2x3+1+1: 1,- 1,- 1,1 " -scale 800% k_lman.gif
![[IM Output]](../static/img/morphology/k_lman.gif)
ご覧の通り、ユーザーカーネルの指定は非常に柔軟であり、多くの端数を持つ畳み込みカーネルであれ、モルフォロジー手法のための「近傍に含まれない」要素を持つ形状カーネルであれ、ほぼあらゆる種類のカーネルを思いのままに指定できます。
画像をユーザーカーネルに変換する
自作カーネルを生成しやすくするため、"[image2kernel](../static/img/scripts/image2kernel)" スクリプトを使ってカーネルを作成できます。例えば、ここでは小さな旗(
)をユーザーカーネルデータファイル("[flag_kernel.dat](../static/img/morphology/flag_kernel.dat)")に変換し、それを使って、いくつかのピクセルを含む画像を膨張させます。
magick -size 80x80 xc:black -fill white \
-draw 'point 20,15 point 55,30 point 40,60' points_pixels.gif
image2kernel -qgm flag.gif flag_kernel.dat
magick points_pixels.gif \
-morphology Dilate @flag_kernel.dat \
flagged_points.gif
下記の カーネルの画像生成 も参照してください。これはカーネルの画像(または拡大した見栄えの良い版)を生成できます。この技法は シンボル描画の代替手段 でも触れられています。
モルフォロジー演算の反復(繰り返し)
これまで見てきたように、より大きなカーネルを生成して、より大きな近傍にわたってモルフォロジーを適用できます。しかしほとんどの場合、より大きなカーネルを使うことの高速な代替手段は、単にモルフォロジー演算子を複数回繰り返す(反復する、ループする)ことです。これは、その演算子の効果がより遠くまで及び、より大きなカーネルを使うのと同じ基本効果を持ちながら、より大きなカーネルを使うことの追加計算コストを伴いません。 例えば、以下は 'Diamond:3' カーネルを使った単一ピクセルの膨張です… |
magick pixel.gif -morphology Dilate Diamond:3 -scale 800% k_diamond_x3.gif
![[IM Output]](../static/img/morphology/k_diamond_x3.gif)
しかし、より小さな 'Diamond' カーネル(半径 1)を3回使っても、同じ結果を得られます… |
magick pixel.gif -morphology Dilate Diamond \
-morphology Dilate Diamond \
-morphology Dilate Diamond -scale 800% k_diamond_x3.gif
![[IM Output]](../static/img/morphology/k_diamond_x3.gif)
依然として非常に小さな 3x3 カーネルを使っているだけですが、基本のモルフォロジー演算を3回繰り返すことで、より大きなカーネルを使ったかのような同じ効果を生み出しています。実際、このように小さなカーネルを繰り返すほうが、はるかに大きなカーネルを使うよりかなり高速です。 | _大きな 'Diamond:3' カーネルは、画像の1ピクセルあたり処理すべき 81 個の要素を持ちます。しかし、より小さな 'Diamond' カーネルを3回繰り返すと、画像の1ピクセルあたり 3×9、つまり 27 個のカーネル要素を処理することになります。この場合は3倍高速です。
この場合の速度向上はそれほど大きくありませんが、カーネルのサイズが増すにつれて節約はずっと大きくなります。_
---|---
モルフォロジー演算を繰り返すのは非常によくあることなので、演算を複数回繰り返す代わりに、IM にその回数だけ演算をループ(反復)するよう指示するだけで済みます。 |
magick pixel.gif -morphology Dilate:3 Diamond -scale 800% k_diamond_3.gif
![[IM Output]](../static/img/morphology/k_diamond_3.gif)
これと最初の例の違いに注目してください。起きたことは、':3' を 'Diamond' カーネルの半径から、'Dilate' 手法を使う回数へと移しただけです。'iteration ' を使って実効近傍を大きくすることは、'Square' や 'Diamond' のような「円形」または「凸」のカーネルのほとんどで機能します。しかし すべてのカーネル種別で機能するわけではありません。例えば、'Plus'(これは「凹」形状ではありません)のような非凸カーネルでは、非常に独特な結果を生みます。 例えば、これは 'Plus'(半径 2)から2倍サイズの 'Plus:4' カーネルへ移行するのとは同じではありません… |
magick pixel.gif -morphology Dilate:2 Plus -scale 800% k_plus_2.gif
![[IM Output]](../static/img/morphology/k_plus_2.gif)
'iteration ' 回数に '0' を使うと、モルフォロジーは何もしないことに注意してください。これは、演算子に何もさせたくないが、コマンドラインから削除したくない場合に、演算子を「オフにする」のに有用な方法です。反復回数ゼロのもう一つの用途については、下記の 詳細出力の表示 を参照してください。特殊な値 '-1' を使うと、画像にこれ以上の変化が見られなくなるまで演算を繰り返します。すなわち、画像が「収束」点に達するまでです。ただしこれは危険で、状況によっては非常に長時間実行される演算につながる可能性があります。例えば 'Dilate' のような演算では、画像全体が完全に白で埋まるまで膨張を繰り返すだけになります。基本的に、一種の暴走する「フラッドフィル」を生み出します(下記の次の例を参照)。'Disk' カーネルを反復してより大きな近傍効果を生み出すことも、一般には推奨されません。それは、'Disk' カーネルは半径が大きくなるほどより正確なディスク形状になるのに対し、反復されたディスクは形状だけでなくカーネルの誤差(非ディスク形状)も拡大してしまうからです。したがって、演算を反復する(より歪んだディスクを生む)より、より大きな半径を使う(より遅い)ほうが良いかもしれません。ただし 'Disk' の半径が本当に大きくなると、半径と複数回の反復の組み合わせが、より高速でなお許容できる結果を生むことがあります。あなたの具体的な状況に対しては、注意と若干の実験が必要かもしれません。
変化の詳細出力
反復(繰り返し)モルフォロジー演算の結果を見たい場合、"[-define debug=True](https://imagemagick.org/command-line-options/#define)" オプションを設定できます。これは 詳細な演算制御 をオンにします。モルフォロジー演算子が反復するにつれて、反復回数の増分カウントと、各反復ステップで画像内の何ピクセルが変化したかを報告します。出力は標準エラーへ送られるので、画像結果はパイプで渡せます。例えば、より大きな 'Octagon' カーネルを使って、画像全体が白で埋まり、これ以上画像に変化を加えられなくなるまで、単一ピクセル画像を 'Dilate' してみましょう。反復上限 '-1' は、永久に、あるいはこれ以上変化が見られなくなるまで反復することを意味することを思い出してください。 | |
MAGICK_THREAD_LIMIT=1 \
magick pixel.gif -define debug=true -morphology Dilate:-1 Octagon \
-scale 800% iterate_infinite.gif
各反復で加えられた変化の数に注目してください。最初は 20 ピクセルが黒から白に変換されました。次の反復ではさらに 48 ピクセル、という具合です。この数は結果の形状の縁が大きくなるにつれて一般に増えますが、形状が画像境界に達すると再び縮み始めます。4回目の膨張では、最後の4ピクセル(画像の角にある)が埋められました。最後の膨張(反復 5)では画像はすでに完全に埋まっていたので、どのピクセルにもこれ以上の変化は行われませんでした。変化が加えられなかったので、モルフォロジーは自動的に中断し、この演算段階の最終的な変化数を出します。'-1' の無限反復を使っても、内部的な上限があります。これは現在、画像の幅または高さの最大値に設定されています。これは ImageMagick が終わらないループに陥るのを防ぐために行われています。ただし典型的には、演算はその内部上限に達するずっと前に終わります。一部のモルフォロジー手法は、実際にはより単純で原始的な手法によって定義されています。例えば '[Smooth](#smooth)' 手法はそうした複合手法の一つです。この手法を使うときに生成される "[-define](https://imagemagick.org/command-line-options/#debug)" 出力は、その処理を構成する複数の内部ステップを示します。
MAGICK_THREAD_LIMIT=1 \
magick man.gif -define debug=true -morphology Smooth:2 Diamond null:
よく見ると、'[Smooth](#smooth)' が実際には4つのより原始的な手法を反復し、要求された演算を行うために画像を内部的に8回処理していることがわかります。各行は次のもので構成されます…
Smooth:_i_._s_これは画像に適用されている高レベルのモルフォロジー手法と、IM が処理している反復回数 'i ' および原始的「ステージ」 's ' を示します。'Smooth' 手法では、ユーザーが与えた「反復回数」は低レベルの原始手法で適用されるため、最初の数は常に '1' です。他の手法では、ユーザーが与えた反復が、低レベルではなくこの高レベルで適用される場合もあります。2番目の「ステージ」番号は、適用されている原始的な「ステージ」のカウントです。'Smooth' 自体は、'[Open](#open)' と '[Close](#close)' の複合手法を実装するため、4つのそうしたステージで構成されます。Dilate*:_i_._k_これは適用されている原始手法です。最初の数 i は再びユーザーが与えた反復回数(それがここで適用されている場合)です。2番目の数 'k ' は原始モルフォロジー手法によって適用されているカーネルです。カーネルは1つしかないので、この場合は常にゼロです。(下記 複数カーネルの扱い を参照)'*' は、カーネルが原始モルフォロジーによって適用される前に反転された(または原点周りに180度回転された)ことを示します。これは一部の複合モルフォロジー手法で必要であり、この場合 '[Close](#close)' 手法は、'[Dilate](#dilate)' および '[Erode](#erode)' 原始手法の使用において常に反転されたカーネルを使います。#6 => Changed 311 Total 637これは画像にモルフォロジー原始手法を適用した結果のレポートです。「ハッシュ」番号は、画像を通過した原始的パスの数の増分カウントです。これは、複合モルフォロジー演算子がどれほど計算集約的かについて良い感触を与えてくれます。次に、そのパス中に何らかの形で変化した実際のピクセル数が得られます。これがこの特定の原始手法とカーネルに対する一連の反復の最後であれば、ピクセル変更の総数も出力されます。ただしこれは、最初から最後までで変化したピクセルの総数を反映するものではなく、特定の原始手法・カーネル演算の低レベル反復が引き起こした変化のみです。一部のモルフォロジー原始手法では、いくつかのピクセルが複数回変化することがあります。
上記から、IM が与えられたモルフォロジー手法を完全に処理するために内部的に4つの処理ループを適用することがあるのがわかるでしょう。ただし、典型的にはこれらのループのほとんどは1回だけ適用されます。 | _警告:変化したピクセル数は、現代のマルチコアマシンでマルチスレッド環境を実行している場合、正しくないことがあります! それはシングルスレッド環境で実行されたときにのみ正確であることが保証されます。私はこれをバグと分類しますが、致命的なものではありません。
これが問題になる場合は、上の最後の2つの例で私がそうしたように、その特定の ImageMagick 実行に対して環境変数 "MAGICK_THREAD_LIMIT" を '1' に設定してください。
IM v6.8.4 以降は、マルチスレッド環境でカウントが正しく扱われるため、"MAGICK_THREAD_LIMIT" の環境設定はもう必要ありません。
生成されたカーネルの表示(デバッグ用)
生成された特定のカーネルを定義するのに使われた値を実際に見たい場合、特殊な設定を定義できます…
-define morphology:showkernel=1 -define convolve:showkernel=1
上記の定義のいずれかにより、IM は、生成されたカーネルがその使用準備のために完全に処理された後で、そのカーネルに関するすべての情報を(「標準エラー」へ)出力します。(畳み込みカーネルのスケーリング を参照)。例えば、以下は組み込み '[Disk](#disk)' カーネルの実際の値です…
magick xc: -define morphology:showkernel=1 -morphology Dilate:0 Disk null:
カーネルを表示したいだけなので、画像処理についてはまったく気にしていないことに注意してください。そこで、モルフォロジーの 'iteration ' を '0'(何もしない)に設定し、また null: 出力ファイル形式を使って画像結果を破棄しています。上記の特殊な浮動小数点値 'nan' は、ユーザー定義カーネル を入力するときと同じ意味を持ちます。それは「数でない(Not A Number)」を意味し、近傍の一部ではないカーネルの部分をマークします。これらの値はすべてのモルフォロジー演算で無視されます。以下は別の例です。今回は '[Comet](convolve.html#comet)' 畳み込みカーネルです。
magick xc: -define morphology:showkernel=1 -morphology Dilate:0 Comet:0x2 null:
これは実は1次元ガウス曲線の半分(シグマ 1.0)であり、ImageMagick からそうした曲線を抽出する良い方法になります。また、この特定のカーネルの「原点」(影響を及ぼすピクセル)が中央からずれている(+0+0 に位置する)ことにも注目してください。これはあまり一般的ではありません。出力中の値のサイズと間隔は、特殊な 精度の演算制御 で制御できます。これはモルフォロジー演算子とほぼ同じ時期に IM に追加されました。例えば、以下は前の例の繰り返しですが、"[-precision](https://imagemagick.org/command-line-options/#precision)" を使って有効桁数をデフォルトの 6 から 3 に制限しています。
magick xc: -define morphology:showkernel=1 -precision 3 \
-morphology Dilate:0 Comet:0x2 null:
| "[-precision](https://imagemagick.org/command-line-options/#precision)" オプションは、モルフォロジーの開発サイクル中の ImageMagick バージョン 6.5.9-1 で追加されました。したがって、モルフォロジーが利用可能であれば、precision も利用可能とみなせます。
---|---
カーネルの画像生成
カーネルを見やすくするため、単一ピクセル画像に Dilate や 畳み込み を使って何が生成されるかを見るのではなく、"[kernel2image](../static/img/scripts/kernel2image)" という特殊なスクリプトを作りました。このスクリプトは、正確な カーネル表示 の出力を抽出し、それをカーネルの画像に変換します。"[kernel2image](../static/img/scripts/kernel2image)" スクリプトには多くのオプションがあり、カーネルの生画像を出力する(デフォルト)ことから、スケーリング量、ピクセル間の隙間、モンタージュ、ラベル付け、さらには結果の「カーネル画像」の着色まで指定できます。このスクリプトにより、各種カーネルを見て理解することがずっと容易になり、これらの例ページに表示するカーネル画像を生成するのに広く使われています。 例えば、以下は "[Octagon](#octagon)" カーネル画像を生成した方法です。 |
kernel2image -10.1 -m "Octagon" kernel_octagon.gif
![[IM Output]](../static/img/morphology/kernel_octagon.gif)
特殊オプション '-10.1' は、すべてのピクセルを 10 ピクセルのサイズにスケールするが、それらのピクセル間に 1 ピクセルの隙間も含める、という意味です。カーネルが十分にスケールされていれば、カーネルの「原点」がいくつかの描画された円でマークされます。'-m' は、抽出された "[Octagon](#octagon)" カーネルの識別ラベルと影効果を付けた画像の モンタージュ を作成するよう指定します。 そしてここでは、上で使った 'L' 字形のユーザー定義カーネルの「カーネル画像」を生成します。 |
kernel2image -20.2 -ml 'L-Shape' "3: 1,-,- 1,-,- 1,1,- " kernel_lman.gif
![[IM Output]](../static/img/morphology/kernel_lman.gif)
既存の画像からカーネルを作りたい場合、"[image2kernel](../static/img/scripts/image2kernel)" スクリプトを使って画像からカーネルデータファイルを作れます。このスクリプトは通常グレースケール画像を取りますが、多色画像が与えられた場合は、画像の各チャンネルが別々のカーネルデータファイルに変換されます。 ここでは、小さな旗の画像(
)から ユーザーカーネルデータ を作り、次に "[kernel2image](../static/img/scripts/kernel2image)" を使ってそのデータを表示用に拡大された「カーネル画像」へ戻します。 |
image2kernel -qgm flag.gif flag_kernel.dat
kernel2image -6.1 -m -ml "Flag" @flag_kernel.dat kernel_flag.gif
![[IM Output]](../static/img/morphology/kernel_flag.gif)
余談:類似のスクリプト "[enlarge_image](../static/img/scripts/enlarge_image)" を使えば、小さな画像の「拡大」版をもっと直接的に生成できたでしょうが、それでは画像を表示することになり、カーネルデータ "[flag_kernel.dat](../static/img/morphology/flag_kernel.dat)" を表示することにはなりません。
複数カーネルリストの扱い
複数のカーネルを生成する
IM v6.6.2-0 以降、画像に対して一度に1つずつ適用される複数のカーネルを指定できます。複数のカーネルを指定するには、各カーネル定義をセミコロン ';' で区切って単に連結します。末尾の最後のセミコロンは省略可能です。例えば、ここでは「コーナー」ピクセルの「パターンマッチング」に使えるリストを含む特殊なカーネルリストを定義します。
3: 0,0,- 0,1,1 -,1,- ;
3: -,0,0 1,1,1 -,1,- ;
3: -,1,- 1,1,0 -,0,0 ;
3: -,1,- 0,1,1 0,0,- ;
カーネル指定の間に少なくとも1つ与えられていれば、余分なセミコロン(';')は問題になりません。また、どのカーネル指定でも余分な空白(改行を含む)は問題になりません。以下はこの定義の カーネル表示出力 です。
magick xc: -define morphology:showkernel=1 -morphology Dilate:0 \
" 3: 0,0,- 0,1,1 -,1,- ;
3: -,1,- 1,1,0 -,0,0 ;
3: -,0,0 1,1,1 -,1,- ;
3: -,1,- 0,1,1 0,0,- ; " null:
そして以下は、特殊な "[kernel2image](../static/img/scripts/kernel2image)" スクリプトを使った、これら4つのカーネルの カーネル画像 です。
kernel2image -20.2 -ml '' -mt x1 \
" 3: 0,0,- 0,1,1 -,1,- ;
3: -,1,- 1,1,0 -,0,0 ;
3: -,0,0 1,1,0 -,1,- ;
3: -,1,- 0,1,1 0,0,- ; " kernel_multi.gif
さて、この定義は実際には、それぞれ90度ずつ回転された4つのカーネルのセットを形成するように展開された、たった1つのカーネルで構成されています。余談:この定義は、特殊な '[Corners](#corners)' パターンマッチングカーネル(後述)とほぼ同等です。ただし、これは背景・前景を問わない任意のコーナーではなく、実際の形状のコーナーに限定される点が異なります。
回転カーネルリストへの展開
IM v6.2.2-0 以降、名前付きカーネルまたはユーザー定義カーネルのいずれかにおいて、3つの特殊フラグの1つを使うことで、単一のカーネルを回転されたカーネルのリストへ展開するよう IM に指示できます。3つの特殊フラグは次の通りです…
' @'3x3 カーネルを45度刻みで巡回的に回転させ、最大8個の回転カーネルのリストを生成します。(覚え方:' @' は円形)' >'(正方形または線形カーネルのみ)90度刻みで回転させます。(覚え方:' >' は直角)' <'これも90度回転を生成しますが、「ミラー」順序で生成します(回転角 0, 180, -90, +90)。この特殊な形の回転展開は、' [Thinning](#thinning)' のようなモルフォロジー手法に対してよりうまく働きます。(覚え方:'<' は直角のミラー)
例えば、上のあの同じカーネルは次のようにもっと簡単に指定できます…
' 3>: 0,0,- 0,1,1 -,1,- '
これは1つのカーネルを定義し、'>' フラグが IM にそれを90度回転リストへ展開するよう指示します。そして以下は、結果として得られる複数カーネルリストの画像です。
kernel2image -20.2 -ml '' -mt x1 \
'3>: 0,0,- 0,1,1 -,1,- ' kernel_rotated_list.gif
そしてここでは、3x3 カーネルを「巡回的」な45度回転で回転させ、8個のカーネルのリストへ展開します。
kernel2image -20.2 -ml '' -mt x1 \
'3@: -,1,- -,0,- 1,1,1 ' kernel_rotated_list2.gif
IM の任意の「単一の」組み込み名前付きカーネルに対しても、それらのカーネルの引数部分で同じフラグを使って同じことができます。例えば、ここでは対称な '[Blur](convolve.html#blur)' カーネルを取り、'>' フラグを使って90度回転リストへ展開します。
kernel2image -12.1 -n -ml '' "Blur:0x1>" blur_kernels.gif
3番目のカーネルは最初のカーネルをそっくり再生するだけになるため、2つのカーネルしか生成されなかったことに注意してください。これが検出され、回転カーネルの生成は停止します。ただし「原点」が中央からずれている場合は、カーネルの「形状」が一致しても原点の位置は同じにならないため、4つの回転カーネルの完全な並びが生成されることになります。多くの組み込みカーネル定義は自動的に複数カーネルリストを生成するので、その目的のためにフラグを指定する必要はありません。すなわち、回転展開は特定のカーネル定義に「組み込まれて」います。そうしたカーネルは典型的に、元の単一カーネル定義の「サブタイプ」も提供しており、特定の用途のために特定のカーネルを選び取れます。
複数カーネル結果のマージ:再反復またはコンポーズ
複数のカーネルを定義した場合、モルフォロジー手法は、複数のカーネルが生成した結果をどうマージすべきかも知る必要があります。これはグローバルな Define の使用で制御できます…
-define morphology:compose={_compose_method_}
ほとんどのモルフォロジー手法のデフォルトは 'None' の設定です。これは、与えられたモルフォロジー手法を使って各カーネルが適用された後、結果として得られる画像を次のカーネルのソースとして使うべきだ、という意味です。つまり、あるカーネルの適用で得られた画像を、次のカーネルのために単に「再反復 」または再利用します。例えば、90度回転された2つの '[Blur](convolve.html#blur)' カーネルを使って Convolve すると、以下が得られます。
magick pixel.gif -morphology Convolve "Blur:0x1>" \
-auto-level blur_re-iterate.gif
ご覧の通り、両方のカーネルが画像に次々と適用され、各カーネルが前のカーネルの結果を扱うようになっています。つまり、あるカーネルの結果を次のカーネルで順番に「再反復 」します。これは2つのステップを次のように行うのと同等です。
magick pixel.gif -morphology Convolve "Blur:0x1" -auto-level blur_1.gif
magick blur_1.gif -morphology Convolve "Blur:0x1+90" \
-auto-level blur_re-iterate.gif
実は、これが Blur 演算子 が画像のぼかしをより速く生成するために実際に動作する方法です。この使い方をより詳しく示す ガウス vs Blur カーネル を参照してください。
'{_compose_method_}' を 'None' 以外の手法に設定すると、演算は 再反復されません。代わりに各カーネルが 元の画像に 適用され、結果として得られる画像が、指定した '{_compose_method_}' 手法を使って 合成(Composite) されます。例えば、別々の結果の 和集合(Union) を生成するために '[Lighten](compose.html#lighten)' モルフォロジー手法を使うと、以下が得られます…
magick pixel.gif -define morphology:compose=Lighten \
-morphology Convolve "Blur:0x1>" \
-auto-level blur_union.gif
これは次のことを行うのと同等でした…
magick pixel.gif -morphology Convolve "Blur:0x1" -auto-level blur_1.gif
magick pixel.gif -morphology Convolve "Blur:0x1+90" -auto-level blur_2.gif
magick blur_1.gif blur_2.gif -compose Lighten -composite \
-auto-level blur_union.gif
モルフォロジー中に IM が実際に何をしているか分からない場合は、変化の詳細出力 をオンにしてください。例えば、以下は各カーネルでの再反復の詳細出力です…
magick pixel.gif -define morphology:compose=None \
-define debug=true -morphology Convolve "Blur:0x1>" null:
そして以下は、各カーネル結果の 和集合(Lighten 合成)の詳細出力です…
magick pixel.gif -define morphology:compose=Lighten \
-define debug=true -morphology Convolve "Blur:0x1>" null:
いずれも、ImageMagick が最終画像を生成するために何をしているかを明確に示しています。小数点以下の数字は、各ステップで適用されているカーネル番号を表します。最後に、'morphology:compose' 設定に従って画像をどう合成するかが続きます。多くの 数学的合成手法 と、その等価な 集合論 型の演算は、各カーネルを元の画像に適用した結果をマージするのにも使えます。要約すると、この設定は、複数カーネルリストの個々のカーネルが、与えられた画像にどう適用されるかを定義します。デフォルトは compose 値 'None' で、単に結果を「再反復」することを意味し、それ以外の場合は、与えられた compose 手法に基づいてすべての結果をマージします。
基本のモルフォロジー手法
モルフォロジー手法は、画像内のオブジェクトの形状を見つけ、解析するための画像処理技法です。拡大、縮小、特定形状の位置特定、などです。これはもともと二値(純粋な白黒)画像を念頭に開発され、そのため最も一般的には、単純な白黒の形状を含む しきい値処理された 画像に適用されます。慣例として、二値画像では白が前景を、黒が背景を表します。そのため手法名はこの慣例に従って記述されています。とはいえ、これらの演算子がグレースケール画像や、場合によってはカラー画像で機能しないというわけではありませんが、その本来の目的は二値形状を扱うことでした。上ですでに見た基本の 形状カーネル は、モルフォロジー手法のための最も一般的に使われる近傍定義「形状」です。そうしたカーネルは、画像内の形状の構造を判定するために典型的に使われるため、しばしば「構造要素」と呼ばれます。
Erode(収縮)( )
名前が示す通り、'**Erode**' 手法は、白い形状を背景ピクセルから「削り取り」、それを小さくします。これは画像の黒い領域を拡張すると考えることもできます。例えば、以下は '[Octagon](#octagon)' カーネルを使って収縮された、単純な二値の「人型」形状です。
magick man.gif -morphology Erode Octagon erode_man.gif
その基本的な効果は、画像が持ちうる突起や尖りを細くしたり完全に除去したりすることですが、同時に画像内に存在する穴(この画像の「腕」によって生じたようなもの)を大きくします。一般に、除去されるピクセルの数はカーネルのサイズによって決まります。
Dilate(膨張)( )
'**Dilate**' 手法は 'Erode' の双対です。これは白い形状を拡張し、指定されたカーネル(および反復回数)に従って形状を大きくします。もちろんこれは、画像の黒い領域を「収縮」させることも意味します。
magick man.gif -morphology Dilate Octagon dilate_man.gif
形状が大きくなるだけでなく、その輪郭が滑らかになることに注目してください。「脚」の間の大きなくぼみが埋められ、画像が含んでいた小さな単一ピクセルの「穴」も同様でした。カーネルのサイズと形状が、画像の縁の周りに追加されるピクセルの数を決めます。 'Dilate' と 'Erode' は双対です。すなわち、(少なくとも対称なカーネルでは)モルフォロジー手法を適用する前後に画像を反転(ネガ化)すれば、もう一方の形の演算子を実際に実行することになります。例えば、ここでは 反転された画像 に 'Dilate' を使って収縮を行います。 |
magick man.gif -negate \
-morphology Dilate Octagon -negate dilate_man_neg.gif
Open(オープニング)( )
以下は '**Open**' 手法の効果ですが、今回ははるかに大きな '[Disk](#disk)' カーネルを使っています。
magick man.gif -morphology Open Disk open_man.gif
その結果、'Open' が輪郭を滑らかにし、鋭い尖りを丸め、使ったカーネルの形状より小さい部分をすべて除去したことがわかります。また、細い橋渡しを切断したり「開いたり」もします。ただし、画像内に存在する「穴」や隙間、たとえば形状の「脚」の間にあるようなものは除去しません。また、形状の基本的な「コア」サイズを大きくも小さくもしません。実際のところそれが行うのは、画像を '[Erode](#erode)' してから、与えられたのと同じカーネルを使って再び '[Dilate](#dilate)' することです。
magick man.gif -morphology Erode Disk open_erode.gif
magick open_erode.gif -morphology Dilate Disk open_man_2.gif
すでにオープニングされた形状に対して、同じカーネルで 'Open' を実行しても、形状にそれ以上の変化は生じないことに注意してください。例えば…
magick open_man.gif -morphology Open Disk open_man_twice.gif
すなわち、同じカーネルで 'Open' 演算を繰り返しても、結果には影響しません。 このため、与えられた iteration 回数は、手法全体ではなく個々の dilate と erode のサブ手法に適用されます。これにより、複合演算を無駄に繰り返すのではなく、反復を実効カーネルの「拡張」に使えます。すなわち、'Open:2 ' の反復は、実際には 'Erode:2' に続けて 'Dilate:2' として画像に適用されます。これは一般に、カーネルによって定義される実効「近傍」を大きくする効果を持ちます。 |
magick man.gif -morphology Open:2 Disk open_man_x2.gif
![[IM Output]](../static/img/morphology/open_man_x2.gif)
ここでは、結果として得られたより大きな近傍によって、人の「頭」と「足」の両端が除去されたことがわかります。形状の主要な本体は基本的に無傷ですが、見た目もより滑らかになり、一方で脚の隙間は手つかずのままです。これはカーネルのサイズを2倍にするのと同じ効果ですが、その正確な形状は、半径を2倍にしたカーネルとまったく同じにはならないかもしれません。
Close(クロージング)( )
'**Close**' 手法の基本的な用途は、カーネル「構造要素」のサイズ程度の「穴」や「隙間」を縮小または除去することです。すなわち、そのサイズ程度の背景の部分を「閉じる」ことです。
magick man.gif -morphology Close Disk close_man.gif
この演算子の基本的な効果は、(穴を閉じることや)くぼみを埋めることで形状の輪郭を滑らかにすることです。また、カーネルが両方に同時に触れられるほど近い他の形状への接続「橋」も形成します。ただし、形状の基本的な「コア」サイズを大きくも小さくもしません。実際のところそれが行うのは、画像を '[Dilate](#dilate)' してから、与えられたのと同じカーネルを使って再び '[Erode](#erode)' することで、画像をまず大きくし、次に小さくします。これは '[Open](#open)' が行うのとは逆の順序です。
magick man.gif -morphology Dilate Disk close_dilate.gif
magick close_dilate.gif -morphology Erode Disk close_man_2.gif
結果として、画像の外側の尖りはそのまま残されますが、「入り江」状のくぼみは滑らかにされて厚くなり、「穴」や「隙間」は閉じられます。互いに非常に近い切り離されたオブジェクトは連結されることがあります。'[Open](#open)' と同様、同じカーネルで '[Close](#close)' 手法を繰り返しても、画像にそれ以上の変化は生じません。ただし演算子に 'iteration ' を使うと内部のサブ手法を繰り返し、より大きなカーネルを使うのと同様に、より強い丸め効果を生み出します。 そして '[Dilate](#dilate)' と '[Erode](#erode)' 手法と同様、'[Open](#open)' と '[Close](#close)' 手法は双対です。演算の前後に画像を 反転(ネガ化) することで、もう一方の「双対」の効果を再現できます。 |
magick man.gif -negate -morphology Close Disk -negate close_man_neg.gif
Smooth(平滑化)
'**Smooth**' 手法は、形状に対して '[Open](#open)' に続けて '[Close](#close)' を適用します。これはまず「小さなオブジェクト」を除去し、次にカーネル「構造要素」のサイズ程度の「穴」や「隙間」を埋めます。ここでは、中程度の '[Octagon:3](#octagon)' カーネルを使って画像を平滑化します。 |
magick man.gif -morphology Smooth Octagon:3 smooth_man.gif
![[IM Output]](../static/img/morphology/smooth_man.gif)
ご覧の通り、すべての「くぼみ」「隙間」「穴」「尖り」が、カーネルのサイズと形状に従って滑らかにされ丸められています。'[Smooth](#smooth)' 演算子は、画像からノイズをゆっくり除去するために、徐々にサイズを大きくした構造要素で繰り返されることもよくあります。除去された部分を保存すれば、画像のモルフォロジー的「分解」が得られ、さらなる研究に使えます。下記の 粒度 を参照してください。この手法は、スキャンされた文書を整理するのに特に適しています。これは実際には、元の画像に4つの別々の「原始」演算を適用していることに注意してください。したがって、単純な '[Erode](#erode)' や '[Dilate](#dilate)' のちょうど4倍遅くなります。
平坦なグレースケールモルフォロジー
4つの基本モルフォロジー手法、およびそれら4手法によって定義される後続の手法は、本質的にすべて二値画像で動作するよう特別に設計されていますが、グレースケール画像とカラー画像の両方に適用できます(ただしカラー画像では奇妙な色効果が生じることがあります)。ここにグレースケール演算の実用例が欲しい ただし、カーネル自体は常に単純な「オン」または「オフ」の近傍とみなされます。'nan' であるか '0.5' 未満のカーネル値は、それが定義する「近傍」の外側とみなされます。要約すると、上記の演算子は「高さ」や「3次元」の特徴を持たない「平坦な」カーネルを適用しますが、それでもグレースケール画像に適用できます。
真のグレースケール(3次元)モルフォロジー
真のグレースケール、すなわち(あるライブラリが言うところの)3次元モルフォロジーは、結果として最大値/最小値を探す前に、実際にカーネルに含まれる値を画像の近隣ピクセルに加算または減算します。これが意味するのは、グレースケール画像を3次元モルフォロジーオブジェクトの「高さフィールド」として扱い、カーネルのグレースケール形状を、その高さフィールドを調整するための平滑化形状として扱うということです。真のグレースケールモルフォロジーの実装の詳細は十分に文書化されていますが、その実用的状況での使い方はそうではありません。すなわち、「平坦な形状のカーネル」を超えて真のグレースケールモルフォロジーを使う有用な例を、「測光(photometric)」処理での使用についての言及以外に、私は見つけられていません。このため、私は真の3次元グレースケールモルフォロジーを実装していません。ただし、もし非平坦なグレースケールモルフォロジー演算子が本当に必要な人がいれば、お知らせください。適切な演算子を実装します。なお、特殊な '[Distance](#distance)' 手法(後述)は、最小の「最小」値を取る前に各ピクセル値にカーネルの値を加算するという点で、実際には真のグレースケールモルフォロジーの動作の仕方に似ています。ただしこの手法は、3D erode(減算して最小を取る)とも dilate(加算して最大を取る)のモルフォロジー定義のどちらにも一致しません。とはいえ非常に密接に関連しており、おそらくそれらの手法を使って実装できるでしょう。
カラー画像向けの輝度版
上記の4つの手法はグレースケールの チャンネル 手法であるため、カラー画像で使うと、あるチャンネルが変更され別のチャンネルが変更されない場合に妙な色効果を生じることがあります。これらは本当は多チャンネルのカラー画像での使用のために設計されておらず、グレースケールおよび二値画像でのみ使うものです。その結果、カラー画像では色が歪み、演算に応じて明るめまたは暗めの色合いになります。これを念頭に、私はこれらの手法の「輝度(Intensity)」版を作りました。'ErodeIntensity'、'DilateIntensity'、'OpenIntensity'、'CloseIntensity' です。これらは定義された「近傍」内のピクセルを比較し、ピクセルの輝度に従って現在のピクセル色を置き換えます。すなわち、個々のチャンネル値だけでなく、カラーピクセル全体がコピーされます。その結果…
輝度版は画像内に「新しい」色を一切生成しません。
その性質上、輝度手法 は現在の "[-channel](https://imagemagick.org/command-line-options/#channel)" 設定を完全に無視します。例えば、ここでは組み込みの "rose:" 画像に対して、'[Dilate](#dilate)' モルフォロジー(明るい領域を拡張)の二値版と輝度版を使います。
magick rose: -morphology Dilate Octagon:3 rose_dilate.gif
magick rose: -morphology DilateIntensity Octagon:3 rose_dilate_intensity.gif
ご覧の通り、通常の '[Dilate](#dilate)' 手法は、各チャンネルが別々に扱われるため、膨張された大きな各斑点で異なる色合いを生成することがあります。一方、2番目の輝度膨張は、最も明るい斑点の完全な色を保ち、それらを真偽値カーネル形状に従って拡張します。輝度手法には省略形の命名法もあり、'Intensity' という語を単なる 'I' に置き換えます。そこでここでは 'CloseIntensity' 手法を使いますが、省略形の 'CloseI' を使います。例えば、以下は組み込みの rose 画像に4つの「輝度」版それぞれを使った結果です。 |
magick rose: -morphology ErodeI Octagon:3 rose_erode_intensity.gif
magick rose: -morphology DilateI Octagon:3 rose_dilate_intensity.gif
magick rose: -morphology OpenI Octagon:3 rose_open_intensity.gif
magick rose: -morphology CloseI Octagon:3 rose_close_intensity.gif
![[IM Output]](../static/img/morphology/rose_close_intensity.gif)
最後の2つは、Paint 演算子 の代替演算子として特に適しているかもしれません。これらの手法は実験的と分類されており、その使用に関するコメントや問題を歓迎します。コメントが寄せられなければ、これ以上は何も追加されません!
代替的な基本モルフォロジー技法
IM v6.5.9-0 より古いバージョンを使っている人でも、いくつかの基本的な
モルフォロジー手法を実装できます。
すべて1の値のカーネルを生成できます。例えば 1 が並ぶ 7x7 の配列
(radius=3)は、極端に大きなシグマを使い、適切な半径を指定して、
ガウスぼかしを使うことで作れます。
したがって
-convolve 1,1,1,1,1,.....
で合計 49 個の 1 は、
-gaussian-blur 3x65535
と同等です。
これにより、二値モルフォロジー手法のための単純な正方形カーネルを
生成できます。
したがって 3x3 の正方形カーネル(radius=1)の 'Dilate' は
-gaussian-blur 1x65535 -threshold 0
'Erode' は
-gaussian-blur 1x65535 -threshold 99.999%
上で前述した通り
'Open' は 'Dilate' に続く 'Erode'
'Close' は 'Erode' に続く 'Dilate'
そして Smooth は 'Open' に続く 'Close'
より大きな正方形カーネルは、より大きな半径を使って指定できます。
残念ながら、他の組み込み形状カーネルは、convolve 演算子を使って
その形状を手動で定義しない限り利用できません。
これも二値モルフォロジーでしか真にうまくいきません。平坦グレースケール
モルフォロジーを実装するには、カーネルの各ピクセルごとに別々の画像を
生成し、それをピクセル位置のために巡回シフトする、別の技法を使う必要が
あります。
しきい値処理した convolve と、巡回シフト合成の両方の手法は、"-morphology"
演算子が ImageMagick に追加されるずっと前に作られた、Fred Weinhaus の
スクリプト "morphology" に実装されています。
Fred Weinhaus の "Morphology" スクリプトの参照とダウンロードは
http://www.fmwconcepts.com/imagemagick/morphology/index.php
差分モルフォロジー手法
モルフォロジー手法の次のレベルは、私が差分モルフォロジーと呼ぶものです。すなわち、これらのモルフォロジー手法の結果は、前述の基本モルフォロジー手法のいずれかと、元の画像、あるいは別のモルフォロジー手法との差分です。本質的に、これらはより単純な手法の1つによって元の画像に加えられた変化を返し、輪郭や、画像間の加算・減算を与えます。これらは本質的に、画像結果の '[Difference](compose.html#difference)' または '[Minus](compose.html#minus)' 画像合成です。
EdgeIn
'**EdgeIn**' 手法は「内部勾配(Internal Gradient) 」とも呼ばれ、収縮 が元の画像から除去するピクセルを見つけます。その結果、縁に最も近いが、元の形状の一部だったピクセルが返されます。
magick man.gif -morphology EdgeIn Octagon edgein_man.gif
結果として得られるエッジは、与えたカーネルのおよそ半分のサイズで、'[Octagon](#octagon)' カーネルではかなり厚くなります。より典型的には、形状の単一ピクセルの輪郭を生成するために、はるかに小さな '[Diamond](#diamond)' や '[Square](#square)' カーネルを使うでしょう。アルファチャンネルとともに '[EdgeIn](#edgein)' を使ってエッジピクセルを抽出する例は、塗りつぶし演算子としての Sparse Color に示されています。
EdgeOut
'**EdgeOut**' 手法は「外部勾配(External Gradient) 」とも呼ばれ、画像の 膨張 によって元の画像に追加されたピクセルを見つけます。その結果、形状のすぐ隣にある背景ピクセルが返されます。
magick man.gif -morphology EdgeOut Octagon edgeout_man.gif
アルファチャンネルとともに '[EdgeOut](#edgeout)' を使う例は、アウトラインまたはハロー透明 に示されています。
Edge またはモルフォロジー勾配
'**Edge**' 手法は「モルフォロジー勾配(Morphological Gradient) 」を返します。これは、直前の2つの「エッジ」手法の加算、あるいはより具体的には、膨張 された形状から 収縮 された形状を引いた差分として記述できます。
magick man.gif -morphology Edge Octagon edge_man.gif
前と同じく、カーネルのサイズと形状が、収縮された画像の厚さを定義します。その厚さは本質的に、そのカーネルサイズから中央ピクセルを引いたものに相当します。したがって、半径 3 のカーネルは一般に 6 ピクセル厚の 'Edge' を生成します(カーネルサイズは 7 ピクセル厚)。 例えば、以下は最小限の '[Diamond](#diamond)' カーネルを使った形状の 'Edge' 輪郭です。 |
magick man.gif -morphology Edge Diamond man_outline.gif
エッジは2ピクセル厚です。これは、元の形状の実際の「ピクセルエッジ」の両側に位置するピクセルを含むからです。このエッジを細くする唯一の方法は、実際には画像全体を半ピクセル分だけ斜めにオフセットすることです。 ![[IM Text]](../static/img/morphology/man_outline.gif)
さまざまな方法で形状の輪郭を得る詳細については、エッジ検出 のセクションを参照してください。将来:「斜め線」を使ったエッジ生成。
Top-Hat
'**TopHat**' 手法、より具体的には「White Top Hat 」は、形状の オープニング によって除去されたピクセルを返します。すなわち、尖りを丸めるために除去されたピクセルと、形状間の接続橋です。
magick man.gif -morphology TopHat Disk tophat_man.gif
ご覧の通り、ピクセルはしばしば、使ったカーネルより厚くなることのない、小さく高度にばらばらの島を形成します。手法名の「Top Hat 」は実は、ここで行ったような二値画像ではなく、グレースケール3次元モルフォロジー手法を使って適用したときの、この演算子の用途を指しています。この演算子は、グレースケール画像とともに使われるほうが一般的です。将来:グレースケール top-hat の例
Bottom-Hat
'**BottomHat**' 手法は「Black TopHat 」とも呼ばれ、形状の クロージング が画像に追加するピクセルです。すなわち、「穴」「隙間」「橋」を埋めるために使われたピクセルです。
magick man.gif -morphology BottomHat Disk bottomhat_man.gif
ここでも、これがピクセルの高度にばらばらな「島」を生じ、そのいずれも使ったカーネルより厚くないことがわかります。ただし、それらは常に前の手法とは完全に異なる島の集合です。将来:グレースケール bottom-hat の例
低レベルのモルフォロジー手法の利用
基本モルフォロジーとチャンネル
上記のすべての基本モルフォロジー手法はチャンネル手法であり、現在の "[-channel](https://imagemagick.org/command-line-options/#channel)" 設定に従って画像の個々のチャンネルに適用されます。これは、未定義の透明領域からの「色漏れ」をあまり気にしないのであれば、これらの手法をカラー画像に適用できることを意味します。例えば、カラーチャンネルを変更せずに、元の「人物図」画像のアルファチャンネルを '[Erode](#erode)' してみましょう。
magick figure.gif -channel A -morphology Erode Diamond:3 \
+channel figure_erode.gif
ご覧の通り、問題なく動作します。他の例については、画像のエッジピクセルを見つけるのに '[EdgeIn](#edgein)' 手法を使う 塗りつぶし演算子としての Sparse Color を参照してください。また、画像の縁を特定の色で拡張するのに '[EdgeOut](#edgeout)' を使う アウトラインまたはハロー透明 も参照してください。
特定の形状の探索
オブジェクトに関する知識は、我々がそれをどのように探査(観察)するかに
依存する。 -- Georges Matheron, モルフォロジーの父
大量の形状の集合から特定の形状を位置特定するために Erode を使う。これを
極限まで進めると [スケルトン](#skeletons) が生成される。[Thinning スケルトン](#thinning_skeleton) も参照。
Open(平滑化された結果)または [条件付き膨張](#dilate_conditional) を使った
オブジェクトの復元。
画像内に見つかったオブジェクトを適切に数えるには、ある種の連結成分解析
(セグメンテーション)が必要。
形状集合の粒度(グラニュラリティ)
徐々にサイズを増やした構造要素で一連の '[Open](#open)' 演算を画像に行い、結果として得られる面積を測定することで、画像内に見つけられるそうした形状の数の要約を素早く得られます。その結果の導関数(傾き)を取ることで、画像を構成するそれらの形状の数とサイズの「スペクトル」が得られます。このグラフが、ある特定の形状に対する画像の「粒度(granularity) 」です。Granulometry(モルフォロジー)、Wikipedia を参照してください。あるサイズから次のサイズへの差は、サイズに基づいて特定の要素を分離・計数することも可能にし、ひいては異なるサイズ・形状の要素を含む領域を分離します。その結果が、テクスチャ・セグメンテーションの手法です。形状集合の数とサイズを判定するデモンストレーション。ただしこれを完全に実装するには「計数」手法(追加予定)が必要です。 歴史的注記… この用途は実は、1960年代にパリの鉱山会社で、もともとのモルフォロジー手法を生み出した本来の原動力でした。これにより作成者たちは、鉱物試料の顕微鏡写真の粒構造を解析して採掘適性を判定する自動システムを作れるようになりました。すなわち、試料中の鉱物のサイズと量を位置特定して数えるのです。例えば、2つの鉱石が同じ量の目的鉱物(通常は岩石中の粒や結晶として)を含んでいても、粒の大きい鉱石だけが効果的に採掘できることがあります。それは、純粋な鉱物を周囲の含鉱石からより容易に分離できるからです。これは非常に労働集約的な作業であり、モルフォロジーがそれをずっと容易にしました。
非対称カーネルの効果(基本手法のテスト)
これらの基本手法が、対称でないカーネルとともに使われたときにどう機能するかを見てみましょう。例えば、ここではユーザー定義の 'L' 字形を、特殊なモルフォロジーテスト画像に適用します(個々のピクセルを見られるよう拡大)。 |
for method in erode dilate open close; do
magick test_morphology.gif \
-morphology $method '2x3+1+1: 1,- 1,- 1,1 ' test_$method.gif
done
'[Erode](#erode)' は、カーネル形状の厳密な一致を、一致点「原点」における単一の白ピクセルにします。また、単一ピクセルの「穴」を、その同じ形状だが「原点」周りに「反射」した形(カーネルを180度回転させたかのよう)に拡張します。
'[Dilate](#dilate)' は、予想通り、画像またはカーネルの「ネガ」かつ「反射」した形に対して、その同じ結果を生みます。単一の白ピクセルがカーネル形状に拡張され、一方、一致する「反射」した形状の穴は、単一ピクセルの「穴」へと縮みます。
また、上記の基本モルフォロジー手法を適用した結果として、テスト画像の正の半分と負の半分の境界が移動することにも注意してください。それは予想されることです。これは、これら2つの手法に関する具体的な点を浮き彫りにします。'[Erode](#erode)' 手法を '[Dilate](#dilate)' に変換する(またはその逆)には、前後で画像を 反転(ネガ化) するだけでなく、原点周りにカーネルを回転または反射させる必要もあります。通常、ほとんどのカーネルは「対称」なので、この2番目の側面は無視できます。これが重要になるのは、ユーザー定義の非対称カーネルの場合だけです。
'[Open](#open)' は、前述の通り、一般に画像内の「穴」を除去しませんが、厳密に一致する形状は変化せずに残ります。より大きな形状(テスト画像の負の半分など)も残ることがありますが、おそらく少し変更されます。
'[Close](#close)' は前のものの厳密なネガ結果ですが、カーネルを反射させる必要がないように(その内部定義によって反射されるため)定義されており、画像をネガ化するだけで済みます。
ヒットアンドミス(HMT)パターンマッチング
ヒットアンドミス( )
'Hit-And-Miss' モルフォロジー手法は、計算機科学の文献で一般に "HMT " として知られ、画像内の特定のパターンを見つけて位置特定するよう特別に設計された高レベルのモルフォロジー手法です。これは「原点」の周囲の「前景」と「背景」ピクセルの特定の配置を探すことでこれを行います。 |
IM v6.6.9-4 以降、この手法を指定するのに 'HitAndMiss'、'Hit_N_Miss'、あるいは単に 'HMT' といった手法名や、その変種のいずれも使えます。このバージョン以前は 'HitAndMiss' という手法名しか使えませんでした。 |
|---|---|
| 例えば、すぐ右側に「背景」ピクセルを持つ「前景」ピクセルを探すことができます。 |
magick man.gif -morphology Hit-and-Miss '2x1:1,0' hmt_right.gif
ご覧の通り、小さな2要素のカーネルは、画像の右側にあったピクセルだけに一致しました。すなわち、この手法は与えられたパターンに一致する特定のピクセルだけを返しました。使われる「カーネル」または「構造要素」は、3種類の要素だけからなるパターンを含むことができます。'1' の値は「前景」を意味し、'0' の値は「背景」を意味し、そして3番目の要素は 'Nan' か '-' か '0.5' の値で指定でき、「気にしない」または「任意のピクセル」を意味します。「原点」にどの値を使うかは非常に重要です。それは、前景の形状だけを「ヒット」したいのか、背景のパターンを「ヒット」したいのかを定義するからです。しかし、「原点」の値を「気にしない」の値に明示的に設定すれば、正しい周囲近傍を持つ前景ピクセルと背景ピクセルのどちらにも一致させられます。例えば、次のような構造要素を使うと…
magick man.gif -morphology Hit-and-Miss '3x1:1,-,0' hmt_right2.gif
内側または外側にある右端のピクセルのいずれも得られます。したがって、形状境界の両側をマークし、2ピクセル幅のエッジを抽出していることになります。ただし、すべてのピクセルがパターンに一致するわけではないので、すべてのピクセルが2重になるわけではありませんが、一般にはそれが得られるものです。「原点」に「気にしない」値を使うことは実は非常に一般的で、特に後で見る Thicken と Thinning 手法では、ピクセルの追加または除去のいずれかに自身を限定します。「気にしない」ことで、同じカーネル定義をどちらの演算にも使えます。演算自体が、どの種類の「ヒット」に関心があるかを定義するからです。
以下は別の例ですが、今回は再び「ヒット」を、形状の内側に入るが北西向きのコーナーを形成するピクセルに限定します。
magick man.gif -morphology HMT "3:0,0,- 0,1,1 -,1,-" hmt_nw_corner.gif
この単一コーナーを '>' フラグの追加で90度回転コーナーのセットに展開すれば、形状内に現れるすべてのコーナーを見つけられます。 |
magick man.gif -morphology HMT "3>:0,0,- 0,1,1 -,1,-" hmt_corners.gif
![[IM Output]](../static/img/morphology/hmt_corners.gif)
ご覧の通り、'[Hit-And-Miss](#hmt)' 手法は、与えられたカーネルパターンのいずれかに一致するすべてのピクセル位置を位置特定して返します。 | _上記の "[-morphology](https://imagemagick.org/command-line-options/#morphology)" 演算の 詳細出力 を調べると、'[Hit-And-Miss](#hmt)' が、与えられた各パターンカーネルに一致するすべてのピクセルの「和集合」を作るために '[Lighten](compose.html#set_theory)' 合成手法を使っていることがわかります。
残念ながら、「変化した」ピクセル数は、各カーネルの適用によってオフにされたすべてのピクセルのものです。言い換えれば、形状内のピクセル数から、各カーネルによって一致したピクセル数を引いたものです。
_
---|---
| _同様に、ヒットアンドミス手法 をそれ自身の結果に対して繰り返すのは通常無意味です。画像が大きく変わってしまい、おそらくその後は何も一致しなくなるからです。
ご覧のように、結果を使って元の画像を変更し、少し異なる画像を生成することはできます。_
---|---
具体的に関心のあるものをより選択的に選ぶカーネルのセットを使えます。例えば、3本の線が交わる点に関心があるとします。その場合、まさにこの目的のために設計された '[LineJunctions](#linejunctions)' カーネルセットを使えます。
magick lines.gif -morphology HMT LineJunctions hmt_junctions.gif
ご覧の通り、わずかな数の位置だけが、そのセット内のいずれかのカーネルに一致します。ただし、結果からは、元の画像内で一致した位置が実際どこにあったのかを実際に見るのが非常に難しくなります。これはグレースケール画像を扱っている場合に特にひどくなります。一つの解決策は、'[Ring](#ring)' のような何らかの 形状カーネル を使って '[Dilate](#dilate)' で一致を拡張することです。例えば… |
magick lines.gif \( +clone \
-morphology HMT LineJunctions \
-morphology Dilate Ring \
-background red -alpha shape \
\) -composite hmt_junctions_rings.gif
![[IM Output]](../static/img/morphology/hmt_junctions_rings.gif)
この特定のカーネルセットが3本以上の線の交点を見つけた位置が、今ははっきり見えます。'[LineJunctions](#linejunctions)' 内の各カーネルは、わずか数か所の特定の位置にしか一致しないことがあるので、このようなパターンマッチングは遅いことがあります。それでも非常に正確で、とてもうまく機能します。もう一つの類似の '[Hit-And-Miss](#hitmiss)' カーネルセットは '[LineEnds](#lineends)' カーネルで、画像内のすべての線の自由端を見つけるのに使えます。 |
magick lines.gif \( +clone \
-morphology HMT LineEnds \
-morphology Dilate Ring \
-background red -alpha shape \
\) -composite hmt_lineends_rings.gif
![[IM Output]](../static/img/morphology/hmt_lineends_rings.gif)
HitandMiss - 前景ピクセルのみ -> erode HitandMiss - 背景のみ -> ネガ化された dilate
グレースケール画像でのヒットアンドミス
'[Hit-And-Miss](#hitmiss)' 手法をグレースケール画像に適用すると、返される実際の値は、最小の「前景」値と最大の「背景」値の差になります。負の結果が生じた場合(数式上)、負には実際の意味がないので、結果は「ゼロにクリップ」されます。言い換えれば、2組のピクセル間の値の「最小分離 」を返します。真偽値の形状では、それは '0.0'(黒)か '1.0'(白)のいずれかになります。しかしグレースケール画像では、これは一致するピクセルの「勾配」に相当します。例えば、一致するパターン内の特定の前景と背景の間にどれだけのコントラストが存在するかを識別するのに使えます。グレースケール画像でパターンに実際に一致するピクセルの真偽値(オン/オフ)の結果だけが本当に欲しい場合は、コマンドの後に "[-threshold](https://imagemagick.org/command-line-options/#threshold) 0" オプションを追加すべきです。
Thicken(形状へのピクセル追加)
'Thicken' 手法は、一致するすべての位置で元の形状にピクセルを追加します。例えば、ここでは形状の右端から2ピクセル離れた背景ピクセルを探します。
magick man.gif -morphology Thicken '3x1+2+0:1,0,0' thick_right.gif
ご覧の通り、形状の元の境界のすぐ外側にピクセルの線ができました。この 'Thicken' 手法を数回 反復 して、その並びを続けられます。 |
magick man.gif -morphology Thicken:4 '3x1+2+0:1,0,0' thick_right2.gif
![[IM Output]](../static/img/morphology/thick_right2.gif)
ただし、ピクセルが追加されるので、パターンマッチングカーネルの原点は前景ピクセルに一致してはなりません。さもなければ、ピクセルがすでに存在する場所にピクセルを追加することになります。上記では、原点ピクセルを背景パターンに設定したので、背景パターンだけが実際に一致します。代替案は、原点を常に「気にしない」要素値に設定することです。そうすることで、同じカーネルパターンを Thicken '[Thicken](#thicken)' に、そして後で見るように '[Thinning](#thinning)' にも使えるようになります。したがって、より良いルールは原点を「気にしない」に設定することです。 | _'[Thicken](#thicken)' 演算を生成するもう一つの方法は、このカーネルの '[Hit-And-Miss](#hitmiss)' を特殊な '[Unity](#unity)' カーネルとともに 和集合 して、結果に元の画像を含めることです。
例えば…_ |
magick man.gif -define morphology:compose=Lighten \
-morphology HitAndMiss 'Unity ; 3x1+2+0:1,0,0' hmt_thicken.gif
_実は、上記の例では 複数カーネルの合成設定 は不要です。'[Hit-And-Miss](#hitmiss)' 手法は、ユーザーが定義しない場合、この合成設定をデフォルトで明示的に設定するからです。
_
典型的には '[Thicken](#thicken)' は、線などの形状を、線を長くせずに大きくするために使われます。'[ConvexHull](#convexhull)' カーネルとして知られる特殊なカーネルセットを使えば、これができます。例えば…
magick -size 80x80 xc:black -fill none -stroke white \
+antialias -draw 'line 10,20 70,60' man_line.gif
magick man_line.gif -morphology Thicken ConvexHull thick_line.gif
Thicken - 八角形の凸包
実際の '[ConvexHull](#convexhull)' カーネルは本当は画像の形状を扱うよう設計されており、形状を「八角形の凸包(Octagonal Convex Hull) 」へと拡張します。すなわち、「八角形」のオブジェクトを生成するまで、両端の間のすべての隙間を埋めようとします。
magick man.gif -morphology Close Diamond \
-morphology Thicken:-1 ConvexHull \
-morphology Close Diamond man_hull_full.gif
![[IM Output]](../static/img/morphology/man_hull_full.gif)
詳細、および2つの '[Close](#close)' 手法が必要な理由については、'[ConvexHull](#convexhull)' カーネルの定義を参照してください。
詳細出力設定 をオンにすれば、反復が行われる様子を観察できます。ただしこれは、上記が非常に非常に遅いことを示します。各 '[Thicken](#thicken)' 反復は、実際には各反復で形状にわずか数ピクセルしか追加しません。したがって、完全な「凸包」が完成するまでに多くの反復がかかることがあります。この具体的なケースでは、画像は8個のカーネルからなる '[ConvexHull](#convexhull)' で 80 回の '[Thicken](#thicken)' 反復を要しました。つまり上記は、実際には 640 回の原始反復に加え、2つの '[Close](#close)' 手法を行うのに必要な別の4回の原始反復を要しました。これにはかなり大きな時間がかかることがあります。基本的に、ヒットアンドミスパターンマッチング を使った反復は非常に非常に「遅い 」ことがあり、代替技法を見つけられるなら、代わりにそれを使うべきです。これを使って、この八角形の形状の生成を引き起こした元画像の点が何だったかを見つけることもできます。凸包の縁と元の形状との交差(Darken 合成)を取ることでです。
magick man_hull_full.gif \
-morphology EdgeIn Diamond man_convex_edge.gif
magick man.gif man_convex_edge.gif \
-compose Darken -composite man_extremities.gif
凸包の内側に収まり、なおかつ上記の凸包の各縁に少なくとも1ピクセルを含むような任意の連結形状は、同じ八角形の凸包を生成します。
グレースケール画像での Thicken
グレースケール画像を扱う場合、'[Thicken](#thicken)' は '[Hit-And-Miss](#hitmiss)' の前景・背景分離の結果を原点ピクセルに 加算 します。したがって、これを使えば、「原点」ピクセルが「背景」集合に含まれていなくても、一致するピクセルを明るくできます。例えば、上のコーナー検出の例を、形状の50%グレー版で繰り返してみましょう。
magick man.gif -evaluate multiply 0.5 man_grey.gif
magick man_grey.gif -morphology Thicken Corners thick_corners.gif
HDRI 版の ImageMagick で '[Thicken](#thicken)' を使う場合は、画像のピクセル値範囲の限界をオーバーフローさせないよう、結果を "[-clamp](https://imagemagick.org/command-line-options/#clamp)" または "[-auto-level](https://imagemagick.org/command-line-options/#auto-level)" するのがおそらく良い考えです。
Thinning( )(形状からのピクセル減算)
'Thinning' 手法は '[Thicken](#thicken)' の双対です。この手法はピクセルを追加するのではなく、元の画像からそれらを減算します。例えば、右端から4ピクセル内側にあるピクセルを除去してみましょう。
magick man.gif -morphology Thinning '5x1+0+0:1,1,1,1,0' thin_right.gif
'Thinning' が適切に機能するには、パターンマッチングカーネルの原点が前景ピクセルを含んでいる必要があります。さもなければ、手法には形状から除去する一致ピクセルがありません。 | _'[Thinning](#thinning)' 演算を生成するもう一つの方法は、'[Hit-And-Miss](#hitmiss)' の結果を元の画像から 相対補集合(MinusSrc 合成を使用)することです。'[Unity](#unity)' カーネルを使うことで、(「減算される」)その画像をカーネルリストの先頭に含められます。
例えば…_ |
magick man.gif -define morphology:compose=MinusSrc \
-morphology HMT 'Unity ; 5x1+0+0:1,1,1,1,0' hmt_thinning.gif
_これは「積集合(intersection)」スタイルの Thinning で、各カーネルのピクセルを順番に除去する「反復」スタイルではなく、すべてのカーネルの指定されたすべてのピクセルを単一ステップで除去します。詳細は Thinning スタイル を参照してください。
_
線の連結性
将来:4連結 vs 8連結の線 IM フォーラムの議論 8連結から4連結の線へ を参照してください。
エッジ検出器出力の Thinning
Thinning の最も一般的な用途の1つは、Sobel 畳み込み のような エッジ検出器 のしきい値出力を、線の全長を保ちながら単一ピクセル厚の線に縮小することです。距離勾配画像を使った例。
スケルトンまでの Thinning
画像の '[Thinning](#thinning)' は実は '[Thicken](#thicken)' よりも一般的に使われます。形状を スケルトン のような、より扱いやすい形に縮小するのに使われるからです。スケルトンは、後で議論するように、形状の任意の2つ(以上)の縁の間の中心線のピクセルを意味します。スケルトンは、非常に複雑な形状の非常に良い記述を提供するので重要です。例えば、画像を処理してループの数、線分、それらがどう配置されているかを見つけることで、手元の形状について多くがわかります。そこで、人物形状の縁を中心線だけが残るまで繰り返し '[Thinning](#thinning)' して、「細線化されたスケルトン 」を生成してみましょう。
magick man.gif -morphology Thinning:-1 Skeleton man_raw_thinned.gif
上記の 詳細 レポートは、18 回の反復、8 個のカーネル、合計 144 回の原始反復を示したでしょう。これは実は、その 凸包 を見つける(上記)よりずっと高速です。Thinning カーネルは各反復で数ピクセルずつではなく、ピクセルの行や列全体を除去するからです。'[Skeleton](#skeleton)' カーネルセットが穴を拡張できず、穴と外側の縁の間の中心線を見つけられなかったことに注目してください。これはこの特定のスケルトン Thinning カーネルの重大な欠点で、これらのカーネルがすべて、何らかの Thinning 一致を行う前に少なくとも背景ピクセルを必要とすることに起因します。この問題を解くために、いくつものスケルトン Thinning カーネルのセットを使えます。より単純な解決策は、カーネルに何か扱う材料を与えるために画像を少し 収縮(Erode) することです。元の形状を青で残すために、「赤」と「緑」チャンネルだけを収縮・細線化します。
magick man.gif -channel RG -morphology Erode Diamond man_erode.gif
magick man_erode.gif -channel RG \
-morphology Thinning:-1 Skeleton +channel man_skeleton.gif
画像内のどの穴も、今やその周りに連続したピクセルのループを生成するように拡張されたことがわかります。 以下は、収縮された穴の周りのループのクローズアップです。 |
magick man_skeleton.gif -crop 22x22+47+29 +repage \
-scale 120x120 man_skeleton_zoom.gif
![[IM Output]](../static/img/morphology/man_skeleton_zoom.gif)
穴と縁の間の正確な中心線を生成しなかったことに注意してください。また、形状が収縮されたため、線は元の形状の縁まで届かず、1ピクセル手前で止まっています。すなわち、線の端が少し「刈り込まれ」ています。それが「収縮」解決策の欠点です。スケルトンはまた八角形の線に限定されており、多くの詳細を欠いていますが、この場合はその単純化が良いこともあります。下記の スケルトン のセクションを参照してください。これは伝統的な '[Skeleton](#skeleton)' カーネルで、ご覧の通り「太い」斜め線を生成し、スケルトンのすべての部分が「4連結」または「ダイヤモンド連結」になるようにします。'[Skeleton](#skeleton)' カーネルには他の変種もあり、結果として得られる「細線化されたスケルトン 」に他の変化を生じます。 より細い、8連結のスケルトン この「伝統的な」スケルトンは前述の通り太い斜めを持ちます。しかしこれでは十分「細く」ないことがよくあります。状況によっては、少し細いスケルトンが欲しいことがあります。すなわち、「4連結」スケルトンではなく「8連結」スケルトンが欲しいのです。 一つの解決策は、'[Skeleton:2](#skeleton2)' カーネル(HIPR2 グラフィックチュートリアルサイト で見つかる)で生成されるような、別のスケルトン生成変種を使うことです。例えば… |
magick man.gif -channel RG -morphology Erode Diamond \
-morphology Thinning:-1 Skeleton:2 +channel man_skeleton_hipr.gif
![[IM Output]](../static/img/morphology/man_skeleton_hipr.gif)
そして以下はループ領域のズームで、結果として得られるスケルトンが、より細い斜めを持つ8連結であることを示しています。 |
magick man_skeleton_hipr.gif -crop 22x22+47+29 +repage \
-scale 120x120 man_skeleton_hipr_zoom.gif
![[IM Output]](../static/img/morphology/man_skeleton_hipr_zoom.gif)
ただし、私はそうしたスケルトンが「伝統的な」スケルトンほど正確でないことを見出しました。基本的に、テストケースで斜めが「間違った側」で細線化されていることがわかりました。基本的に、斜めのどちら側が除去されるかが、形状の性質による判断ではなく、純粋にカーネルセット内の「コーナー」Thinning カーネルの順序によって制御されるからです。
代替案は、「伝統的な」スケルトンを取り、斜めの端点で定義される斜めの「外側」で常に細線化されるように細線化することです。特殊な '[Diagonals](#diagonals)' Thinning カーネルがこれを行うよう設計されており、後で「仕上げ」に '[Corners](#corners)' カーネルが使われます。そこで、前の「伝統的な」スケルトンをさらに細線化してみましょう…
magick man_skeleton.gif -channel RG \
-morphology Thinning:-1 Diagonals \
-morphology Thinning Corners man_thin_skeleton.gif
この、伝統的な4連結スケルトンを細線化する技法は、単に '[Skeleton:2](#skeleton2)' 変種を直接使うより少し遅くなります。追加の細線化には、8個のカーネルの8回の Thinning 反復、つまり 64 回の原始反復が必要でした。あるいは、'[Corners](#corners)' カーネルだけを使うこともできますが、それは「ランダムに」斜めのどちら側が細線化されたかの選択を伴う 'HIPR' 変種を生成するだけです。ただしそれは全4カーネルの1パスしか要しないので、'[Diagonals](#diagonals)' を使うよりずっとずっと高速です。いずれにせよ、「伝統的な」4連結スケルトンから始めることで、その後(何らかの)8連結版を非常に簡単に生成できます。
スケルトン情報
スケルトン(おそらく4連結版と8連結版の両方さえ)が得られたら、次のステップは通常、スケルトンに関するより多くの情報を見つけることです。例えば、「線の自由端」「線の交点」「線のループ」がいくつあるか、などです。線端の数 ここでは、前に生成したスケルトン(「赤」チャンネルから抽出)に対して、'[LineEnds](#lineends)' の ヒットアンドミス探索 を使います。次に、それらの線端を Dilate して Ring にし、着色してから元のスケルトンとマージし、その位置を非常に見やすくします。
magick man_skeleton.gif -channel R -separate +channel \
-morphology HMT LineEnds man_ends.gif
magick man_ends.gif -morphology Dilate Ring -background Red -alpha Shape \
man_skeleton.gif +swap -composite man_ends_marked.gif
互いに連結する線、あるいはピクセルのループへ連結する線は見つからなかったことに注意してください。線の自由端だけが示されました。(ヒストグラム出力 を使って)ピクセル数を数えれば、このスケルトンが 12 個の線端を生成したことがわかるでしょう。線の交点の数 '[LineJunctions](#linejunctions)' カーネルを 8連結スケルトン(できれば、線端の計数に使ったのと同じ元のスケルトンから細線化されたもの)とともに使うことで、画像内の線の交点数のおおまかなカウントを得られます。2つの異なるスケルトン生成変種を混ぜないでください。
magick man_thin_skeleton.gif -channel R -separate +channel \
-morphology HMT LineJunctions man_junctions.gif
magick man_junctions.gif -morphology Dilate Ring \
-background Red -alpha Shape \
man_thin_skeleton.gif +swap -composite man_junctions_marked.gif
このカーネルを伝統的な4連結スケルトンに直接試すと、一部の 'T' 交点で複数の一致が得られ、カウントが非常に不正確になります。ご覧の通り、結果は 12 個の線の交点で、この特定の形状にとっては正しい値です。ただし一部の交点では '[LineJunctions](#linejunctions)' カーネルは不正確です。例えば、4本線の斜め 'X' 交点は1つの一致しか生成しないのに対し、直交する '+' 交点は4つの一致を生成します。これら特殊な交点はいずれも、線交点カウントを正しく保つには2つの一致を生成すべきです。したがって、正確なカウントを得るには、すべての 'X' 交点ごとに1つ値を加え、すべての '+' 交点ごとに2つカウントを引く必要があります。
ループのないスケルトンでは、交点の数は線端の数より2少ないはずです。しかし、線端の数が線の交点の数と等しい場合、それはスケルトンに1つ以上のループがあることを意味します。さて、このスケルトンには 12 個の線端と 12 個の交点があります。したがって、画像のどこかに少なくとも1つの連続したピクセルのループを含んでいます。ループの数 将来:連結オブジェクトのラベリング
線の刈り込み(Pruning)
そこで、この画像には少なくとも1つのループがあることがわかりました。形状をそのループだけに単純化したいとします。解決策は、すべての線端を、すべて除去してしまうまで繰り返し「刈り込む(Prune) 」ことです。このような4連結スケルトンでは、より小さな '[LineEnds](#lineends_subtypes)' カーネルのセットを使って、処理をおよそ2倍速くすることさえできます。
magick man_skeleton.gif -channel G \
-morphology Thinning:-1 'LineEnds:1>' man_loop.gif
これに対する 詳細 レポートは、自由端を持つすべての線を画像から「刈り込む 」のに、4個のカーネルで 75 回の反復、結果として 300 回の原始反復がかかったことを示したでしょう。これは、スケルトンを見つけるのに使ったおよそ2倍の演算数であり、この演算がどれほど集約的になりうるかを示しています。'[LineEnds](#lineends)' カーネルの完全なセット(8個のカーネル)を使っても 75 回の反復がかかったでしょうが、2倍の数のカーネルで、これは 600 回の原始反復になります。
線の高速な刈り込み
高速な完全刈り込み技法…
1/ 線端と線の交点を見つける。
2/ 線の交点を削除してすべての線分を完全に切り離す。
3/ フラッドフィル、または条件付き dilate を使って「線端」セグメントを除去する。
4/ 線の交点を復元する。
5/ それを元画像のマップとして使い「ループ」を復元する。
最初のステップはすでに扱いました… 結果として… |
magick man_skeleton.gif -channel R -separate +channel \
-morphology HMT LineEnds man_ends.gif
![[IM Output]](../static/img/morphology/man_ends.gif)
すべての線分を切り離す(分離する)には、'[LineJunctions](#linejunctions)' カーネルを使えます。ただし、デフォルトのカーネルセットは 'T' 交点を完全には切り離しません(位置特定するだけです)。 すべての線分を適切に切り離すには、直交する 'T' カーネルもカーネルセットに追加する必要があり、'+' 交点も含めるのが最善です。例えば。 |
magick man_skeleton.gif -channel R -separate +channel \
-morphology HMT 'LineJunctions;LineJunctions:3>;LineJunctions:5' \
man_disconnect.gif
![[IM Output]](../static/img/morphology/man_disconnect.gif)
これらの一致で Thinning すると、実際にセグメントが切り離されますが、これはすべて1ステップで行う必要があります(Thinning スタイル を参照)。さもなければ正しく機能しません。 |
magick man_skeleton.gif -channel R -separate +channel \
-define morphology:compose=Darken \
-morphology Thinning 'LineJunctions;LineJunctions:3>;LineJunctions:5' \
man_line_segments.gif
![[IM Output]](../static/img/morphology/man_line_segments.gif)
以下は、切り離されたセグメントを示す「ループ」のズームです。 |
magick man_line_segments.gif -crop 22x22+47+29 +repage \
-scale 120x120 man_line_segments_zoom.gif
![[IM Output]](../static/img/morphology/man_line_segments_zoom.gif)
この時点で、先ほど発見した「線端」と一致を含む線分をすべて除去できます。これは、それらの「シード」点から「フラッドフィル」して削除することで行えます。ただしこれは、フラッドフィルが前提とする4連結スケルトンでのみ機能します。ここに例 あるいは、条件付き膨張 を使ってすべての点を同時に見つけ、それらを除去できます。ここに例 - 条件付き Dilate または Erode が利用可能なとき。 線の交点を復元し、1回刈り込みを行い、残った単一ピクセルを除去すれば、すべての線分を素早く除去したことになります。確かにこれは多くのステップに思えますが、信じてください、同じ結果を得るために「線端を刈り込む 」のを 300 回行わなければならないよりは、まだずっと高速です。
Thinning スタイル - 逐次か同時か
線分の端を1回だけ「刈り込み」、元の画像と比較すると、線の正確な形状と向きに応じて、線分が2回から4回刈り込まれていることが多いとわかるでしょう。例えば(結果画像は拡大)、これはデフォルトの線端 Thinning です。
magick -size 10x10 xc:black -fill white \
+antialias -draw 'line 1,7 8,3' line.gif
magick line.gif -channel GB \
-morphology Thinning LineEnds line_seqential.gif
これは、デフォルトでは各 '[Thinning](#thinning)' カーネルが前のカーネルの結果に対して 逐次的に 適用されるからです。すなわち、あるカーネルが選択したすべてのピクセルを除去してから、次のカーネルをその結果に適用し、それが同じ線端からさらにピクセルを選択する(し、実際にそうなる)かもしれません。言い換えれば、デフォルトでは、与えられたすべてのカーネルを通る1回の完全な「反復」のために、線の端を複数回細線化します。つまり、詳細出力 に頼って、この演算子の1回の反復で除去されたピクセル数を数えることで、すべての線がどれだけ長かったかの正確な感触を得ることはできません。ただし、'[Thinning](#thinning)' の動作を変更して、すべてのカーネルを通る単一の '[Hit-And-Miss](#hitmiss)' 反復が見つけるピクセルの集合だけを除去するようにできます。言い換えれば、反復の開始時にすべてのカーネルを同じ画像に適用し、それらをマージし、そのピクセルだけを、すべてのカーネルに対して一度だけ除去します。すなわち、HMT が選択したすべてのピクセルを 同時に 除去します。基本的に、複数カーネルの合成設定 を '[Darken](compose.html#darken)' 合成手法を使うように設定すると、まさにそれが行われます。具体的には、選択されたすべてのピクセルを、選択されたピクセルの単一の除去のためにマージします。例えば… |
magick line.gif -channel GB -define morphology:compose=darken \
-morphology Thinning LineEnds line_simultaneous.gif
![[IM Output]](../static/img/morphology/line_simultaneous_mag.gif)
ここで起きたのは、パターンマッチングカーネル の各カーネルが元の画像のみに適用されることです。元の画像に一致したピクセルが、まとめて収集されます。すなわち、'darken' 合成を使って、すべてのカーネルの結果と元画像との「積集合(intersection)」だけを除去します。ただし、除去はすべてのカーネルを通る1回の反復に対して、すべて1ステップで行われます。その結果、線端は、複数のカーネルがその線端に一致しうる場合でも、一度しか一致しません。したがって、異なるカーネルによって2つ以上のピクセルが除去されるのではなく、端の単一ピクセルだけが除去されます。要約すると、'[Darken](compose.html#darken)' の 複数カーネルの合成設定 を追加すると、'[Thinning](#thinning)' 手法が「逐次 Thinning 」(一度に1カーネル - デフォルト)ではなく「同時 Thinning 」(すべてのカーネルを同時に)を行うようになります。
ただし、これは線端の刈り込みをより行儀よくする一方で、遅くなり、Thinning の全体的な結果を変えることがあります。いくつかの箱を、左右の縁を同時に細線化して '[Thinning](#thinning)' する場合を考えてみましょう。
magick -size 10x10 xc:black -fill white -draw 'rectangle 4,1 5,7' rect.gif
magick rect.gif -channel GB -define morphology:compose=darken \
-morphology Thinning Edges rect_simultaneous.gif
「同時 Thinning」は、実際には中央の矩形を完全に削除してしまいました! 何が起きているかというと、形状が2ピクセル厚まで細線化され、その後「太い」中央矩形の両側がパターンに一致して、両側が「細線化」されたのです。スケルトンまでの Thinning でこれを行うと同じことが起こります。一方、デフォルトの「逐次 Thinning」は以下を生成しました… |
magick rect.gif -channel GB \
-morphology Thinning Edges rect_seqential.gif
![[IM Output]](../static/img/morphology/rect_seqential_mag.gif)
ご覧の通り、スケルトンの中心線となるピクセルの1つ(右側)を保ちました。これは、あるカーネルのセットが「太い」中心線の片側をまず細線化したが、後のカーネルがこの「より細い」線に一致しなかったので、除去されなかったからです。本質的に、デフォルトの「逐次 Thinning 」が特殊な「同時 Thinning 」より良い状況もあれば、その逆もあります。
パターンマッチングカーネル
前述の通り、「パターンマッチング」または '[Hit-And-Miss](#hitmiss)' カーネルは、前景、背景、「気にしない」の3種類の要素を含むことができます。'1.0' の値(または白)は前景ピクセルに一致します。'0.0' の値(または黒)は背景ピクセルに一致します。近傍の一部でなく、したがって「気にしない」ピクセル要素を表すには、'0.5' の値か、特殊な値 'Nan' か '-' のいずれかを使えます。'[Hit-And-Miss](#hitmiss)' は、最小(minimum)の前景ピクセルが最大(maximum)の背景ピクセルより大きい場所だけに一致します。そして、これら2つの値の差、またはゼロを返します。
Peaks
'Peaks' カーネルは、前に示した '[Ring](#ring)' カーネルの拡張です。2つの半径引数が、中央の「原点」の単一の前景ピクセルを取り囲む背景ピクセルの「リング」を生成します。以下は、より有用な 'Peak' カーネルのいくつかの例です…
上記のカーネルは、より暗いピクセルの海の中の単一ピクセルの「ピーク」値を確定的に位置特定するか、より大きなリングの中に完全に収まる小さな形状を見つけるのに使えます。これらは 相関パターンマッチング探索 のコントラストを改善するのに特に有用です。
Edges
'Edges' カーネルセットは、形状の平坦な縁の上にある任意のピクセルに一致します。鋭い90度のコーナー上のピクセルには一致しませんが、八角形の形状のコーナーピクセルには一致します。
ご覧の通り、すべての90度回転が生成されますが、一般により良い結果を生む「フリップフロップ」ミラー順序で並べられています。典型的にはこのカーネルは画像 '[Thinning](#thinning)' カーネルの一種として使われますが、このままでは斜めの縁を細線化できず、画像の適切なスケルトンを生成できません。例えば…
magick man.gif -channel RG \
-morphology Thinning:-1 Edges thin_edges.gif
下記の '[Skeleton](#skeleton)' カーネルを参照してください。
Corners
'Corner' カーネルは、画像の縁の周りの任意の斜めコーナーピクセルを位置特定します。その使用例については上の '[Hit-And-Miss](#hitmiss)' を参照してください。
例えば、ここではすべての斜めの縁を細線化しようとしてこれを使いました… |
magick man.gif -channel RG \
-morphology Thinning:-1 Corners thin_corners.gif
これは '[Edges](#edges)' カーネルと組み合わせて、1つの手法でスケルトン細線化を行えます。この例については下記の '[Skeleton](#skeleton)' カーネルを参照してください。 ![[IM Output]](../static/img/morphology/thin_corners.gif)
Diagonals
'Diagonals' カーネルは、4連結の斜め線を8連結の斜め線に細線化するために単に '[Corners](#corners)' カーネルを使うことの代替です。これは、完了するまでコーナーから中央に向かって外側のピクセルの集合を除去することで、4連結線を細線化するのに使えます。
結果は、90度コーナーを位置特定して細線化するために '[Corners](#corners)' カーネルを使って仕上げるべきであることに注意してください。使用例については より細いスケルトン を参照してください。 Diagonals サブタイプ 'type[,angle]' 引数をカーネルに与えることで、上記のカーネルセットを構成するのに使われた特定のサブタイプを選択できます。
これにより、斜めを正確に望む通りに細線化するための、独自の特定のカーネルセットを指定できます。例えば、(同じ angle 値で上記両方のカーネルを使って)4種類の斜めをそれぞれ別々に細線化できます。そうすることで、各種類の斜めを一度に1つずつ反復的に縮小し、それら特定の斜めがすべて細線化されたらすぐに中断でき、実行される「原始モルフォロジーステップ 」の全体数を削減できます。例が必要 そのままだと、デフォルトのカーネルセットは単に、すべての斜めが細線化されるまで、すべての斜めを同時かつ繰り返し細線化しようとします。つまり、細線化が必要な斜めだけでなく、すべての斜めが細線化されるまで、すべてのカーネルが適用されます。つまり、もはや不要な多くの「原始モルフォロジーステップ 」を実行し、ほとんどのカーネルが各ループ中に画像へ何の変化も加えません。完全な例が必要 4種類の斜めはそれぞれ、(各特定の角度に対する)両方のカーネルのペアを使って実行すべきであることを思い出してください。これは、斜めが「弧」の一部であるような場合に、各特定の斜めの両端が一緒に細線化されるようにするためです。この種の細線化/太線化演算については、IM フォーラムに関連する議論 8連結から4連結の線へ があります。
LineEnds
'LineEnds' カーネルセットは、上の 線端の刈り込み で示した通り、線の端を位置特定するよう設計されています。より具体的には、鋭い尖りの端を見つけます。
ご覧の通り、少なくとも2ピクセルを持ち、一致するピクセルが背景ピクセルで「覆われた」または「囲まれた」線にだけ一致します。例えば、ここでは '[Hit-And-Miss](#hitmiss)' を使ってすべての線端を見つけます。
magick lines.gif -morphology HMT LineEnds hmt_lineends.gif
そう、この画像にはたくさんの線端があります。ただし、何らかの「ループ」で終わる線は一致を生成しないことに注意すべきです。このカーネルを使って「反復的 Thinning」スタイル(デフォルト)で画像を '[Thinning](#thinning)' する場合、連続するカーネルが同じ線端に2回以上一致し、'[Thinning](#thinning)' 手法全体の1回の反復中に線を何度も縮めることがあります。詳細は Thinning - 逐次 vs 同時 を参照してください。 線端サブタイプ このカーネルにも 'type[,angle]' 引数を与えられ、上記の '[LineEnds](#lineends)' カーネルセットを生成するのに使われた単一カーネル定義の1つを返します。
これらは必要に応じて 回転カーネルリストへ展開 したり、必要に応じて特定の 'angle ' へ回転させたりできます。デフォルトの '[LineEnds](#lineends)' セットは、実際には次のカーネル定義を使います。
'LineEnds:1> ; LineEnds:2>'
'LineEnds:3' は斜めの 'LineEnds:2' に対する直交版で、任意の斜めのコーナーや交点から十分離れた線端だけを見つけます。'LineEnds:4' は伝統的な線端カーネルで、巡回的に回転されて8個のカーネルを生成します(例:'LineEnds:4@')。ただし、直交する 'T' 交点に接続する線の最後のピクセルを位置特定できません。上で定義したデフォルトの '[LineEnds](#lineends)' セットは、しかし同じ数のカーネルを使って、'T' 交点でのその最終ピクセルを見つけます。
LineJunctions
'[LineEnds](#lineends)' が線のグループの端を見つけるのに対し、'LineJunctions' は3本以上の線の交点を形成する点を見つけます。
例えば、ここでは '[Hit-And-Miss](#hitmiss)' を使ってすべての線の交点を見つけます。
magick lines.gif -morphology HMT LineJunctions hmt_junctions.gif
'[LineJunctions](#linejunctions)' カーネルは一般に2つの目的で使われます。
- 画像内の線の交点の数を数え、ひいてはスケルトン内の線分の数のカウントに役立てる。
- すべての線分を互いに切り離す。
ただし、上の画像の 'T' および '+' 交点では、'Y' 交点カーネルが、実際の交差点から1ピクセル離れた点に一致することに注意してください。このため、交点カウントは予想通りにならないことがあり、特に '+' では交点カウントに必要な2つではなく4つの一致が見つかります。注意が推奨されます。これら2つの側面の詳細については、スケルトン情報 と 線の高速刈り込み を参照してください。このカーネルは実際には前景ピクセルだけを定義するので、'[Hit-and-Miss](#hitmiss)' 手法としてではなく、単純に '[Erode](#erode)' 手法として適用できます。 線交点サブタイプ このカーネルも 'type[,angle]' 引数を指定することで、各種サブタイプへのアクセスを提供します。これは特定の種類の線の交点を探すのに使えます。
カーネル 'LineJunctions:2' は 'LineJunctions:3,45' を使っても指定でき、同様に 'LineJunctions:5' と 'LineJunctions:4,45' は等価です。デフォルトの '[LineJunctions](#linejunctions)' カーネルセットは、最初の2つの交点定義('Y' と斜めの 'T' 交点)だけを次のように使います…
'LineJunctions:1@ ; LineJunctions:2>'
これは8連結の線の交点に適しています。IM フォーラム「LineJunctions が使うカーネル」で議論されているように、4連結の線の交点だけをテストしたい場合は、直交する 'T' 交点と '+' 交点を探す必要があります。
'LineJunctions:3> ; LineJunctions:5'
ただし、'T' カーネルは '+' にも一致するので、上記を次だけに切り詰められます…
'LineJunctions:3>'
線分のカウント判定にそれが必要な場合は、4方向の '+' 交点だけのための別の画像テストを使って、3方向の 'T' 交点からそれらを分離できます。
Ridges
'Ridges' カーネルは、距離勾配 画像などにある、稜線(ridge)やピクセルの細い線を位置特定するのに使われます。これらのカーネルは実験的であり、変更される可能性があります。 デフォルトは単一ピクセル厚の稜線を位置特定するよう設計されています。
Ridges:2 2ピクセル厚の稜線を見つけるよう設計された特殊な拡張サブタイプです。この複雑さは、この種の傾いた線を、その線のミラーも含めて位置特定してマークする必要があることに起因します。
このカーネルのセットは、「モルフォロジースケルトン」が実際には1ピクセル厚と2ピクセル厚の両方の線で構成されるため重要です。
ConvexHull
'ConvexHull' カーネルセットは、形状の「八角形の凸包(Octagonal Convex Hull)」を生成するよう、形状を太線化するために設計されています。すなわち、形状全体を収められる最小の八角形の形状です。
90度回転されたカーネルのセットが2つあり、一方が他方のミラー画像です。原点は実際には「背景」要素なので、本当は '[Thicken](#thicken)' パターンカーネルとして使われることだけを意図しています。ただし、このカーネルは、「人物」形状に見られるような水平または垂直の「スロット」を含む画像では失敗します。
magick man.gif -channel R \
-morphology Thicken:-1 ConvexHull man_hull.gif
解決策は、'[ConvexHull](#convexhull)' を使う前に、これらのスロット(と中央の穴)を '[Close](#close)' することです。 |
magick man.gif -morphology Close Diamond \
-morphology Thicken:-1 ConvexHull \
-morphology Close Diamond man_hull_full.gif
![[IM Output]](../static/img/morphology/man_hull_full.gif)
上記では、'[ConvexHull](#convexhull)' を使った後にも '[Close](#close)' を繰り返したことに注意してください。その理由は、画像内の大きな「穴」も '[Thicken](#thicken)' によって単一ピクセルや直交する「スロット」まで縮小されるからです。'[Close](#close)' を繰り返すと、最終的な形状に影響を与えずにそれらの穴を除去できます。 以下は別の例で、元の形状(白)が凸包の太線化(赤)を使って拡張されました。 |
magick circles.gif -channel R \
-morphology Thicken:-1 ConvexHull circles_hull.gif
![[IM Output]](../static/img/morphology/circles_hull.gif)
ご覧の通り、結果は八角形の形状であり、一方、中央の穴は2ピクセルのスロットまで縮小され、閉じる準備が整いました。
Skeleton
特定の形状の細線化によって「スケルトン」を生成するのは容易なことではありません。同じカーネルセットでも、カーネルを並べ替えると最終的な「スケルトン」に異なる変種が生成されます。このため、私は1つの 'Skeleton' カーネルセットだけを実装したのではなく、いくつものセットを実装しており、'type ' 引数の番号を与えることで選択できます。
Skeleton:1
最初でデフォルトのセット '**Skeleton:1**' は、最初に使われた伝統的な細線化カーネルです。これは基本的に上の '[Edges](#edges)' カーネルとまったく同じですが、45度刻みで巡回的に回転されています。
|
magick man.gif -channel RG \
-morphology Thinning:-1 Skeleton thin_skeleton1.gif
![[IM Output]](../static/img/morphology/thin_skeleton1.gif)
結果は形状の妥当な「細線化スケルトン」ですが、斜めは片側で少し太いままになる傾向があります。基本的に、生成されるスケルトンは4連結で、高速刈り込み 技法を使えるようになります。また、このカーネルセットは画像内の単一ピクセルの穴を正しく拡張しないことに注意してください。言い換えれば、その穴の周りのスケルトンは、穴と画像の残りとの間の中心線にすら近くありません。詳細は スケルトンまでの Thinning を参照してください。
Skeleton:2
'**Skeleton:2**' 変種は、伝統的な 'Skeleton:1' 版とほぼまったく同じです。これは HIPR2 画像処理リソース のドキュメントで見つけられました。
|
magick man.gif -channel RG \
-morphology Thinning:-1 Skeleton:2 thin_skeleton2.gif
![[IM Output]](../static/img/morphology/thin_skeleton2.gif)
これを前のセットと比較すると、コーナーの内側のピクセルが除去されていることに気づくでしょう。これにより、Thinning 演算は斜めから余分な太線化を除去できるようになります。ただし、この斜めの細線化は対称ではなく、画像の形状とカーネルが適用される順序に大きく依存します。 'Skeleton:2' 変種は、組み合わせた '**Edges;Corners**' カーネルリストを単に使うのと非常に密接に関連しています。
|
magick man.gif -channel RG \
-morphology Thinning:-1 'Edges;Corners' thin_edge-corner.gif
![[IM Output]](../static/img/morphology/thin_edge-corner.gif)
これと 'Skeleton:2' が使うものとの唯一の違いは、リスト内のカーネルの順序です。同じカーネルのセットが使われたにもかかわらず、結果として得られるスケルトンも異なることに注目してください。これは、細線化によるスケルトン生成が実際にはかなり脆く、単純な順序の変更だけで連結スケルトンに異なる結果を生みうることを示しています。
Skeleton:3
'**Skeleton:3**' は、Dan S. Bloomberg による1991年の研究論文「Connectivity-Preserving Morphological Image Transformations」(下記 ThinSE カーネル を参照)における、Thinning カーネルの使用に関する形式的研究で開発されました。彼はそうしたスケルトンを数多く開発し、研究結果を表にまとめました。以下は、4連結スケルトンを生成するために彼が考え出せた最良のものです。ただし、前のスケルトンと異なり、これは3つの回転カーネル(合計12個)の使用を必要とします。
|
magick man.gif -channel RG \
-morphology Thinning:-1 Skeleton:3 thin_skeleton3.gif
![[IM Output]](../static/img/morphology/thin_skeleton3.gif)
最初の回転カーネルのグループが、ただ1つの背景ピクセルを含んでいることに注意すべきです。これは、このスケルトンが「人物」形状に存在する単一ピクセルの穴を開き、中心線指向のスケルトンを生成できたことを意味します。これはあまり多くの枝を生成せず、きれいで滑らかな線を生み、また完全に細線化されます。総じて、これは優れたスケルトン細線化カーネルの1つです。
ThinSE
Dan S. Bloomberg による同じ研究論文「Connectivity-Preserving Morphological Image Thansformations」は、4連結線または8連結線のいずれかを保つよう設計された、最小限の 3x3「Thinning 構造要素」の完全な範囲を、第一原理から実際に開発しました。'**ThinSE:**{_type_}' カーネルセットは、それらすべての構造要素のリストであり、連結性と保存の強さに基づいてグループ分けして以下に列挙します。'{_type_}' は、研究論文で使われた上付き(連結性)と下付きの要素番号に基づく番号です。したがってカーネル '**ThinSE:41**' は、4連結線保存要素の最初のものです。回転角を追加したり、与えられたカーネル定義に対して回転またはミラー回転されたフラグのセットを生成したりすることもできます。
最後の「汎用 Thinning カーネル 」'**ThinSE:482**' は、エッジ検出カーネル のセットを定義するのに使われたのと同じカーネルだと認識できるかもしれません。この汎用カーネルは、上に示した他のすべての Thinning カーネルがそこから開発された中核のカーネルです。これがセットのデフォルトカーネルです。両方の汎用カーネル 'ThinSE:481' と 'ThinSE:482' が、回転で関連する唯一のカーネルであることに注意してください。すなわち 'ThinSE:481x45' は 'ThinSE:482' と等価です。他の多くの組み込み HMT カーネルセットは、実際には内部的にこれらのカーネルによって定義されています。例えば、カーネルセット '**ThinSE:41 ; ThinSE:42 ; ThinSE:43**' とその回転展開は、'[Skeleton:3](#skeleton3)' セットを作るのに使われる12個のカーネルを生成します。このスケルトンは、良い細線化スケルトンを生成するために見つかった最良のカーネルセットとして、論文に列挙されていました。他のスケルトン生成 Thinning カーネルも、上記のカーネルを使って定義されています。
ただし、'ThinSE:44' のような一部のカーネルは、「連結性」を保つよう設計されているものの、実際には線端を保たず、スケルトンを単一の点や、連結したリングのセットまで刈り込んでしまうので注意してください。すべてのカーネルは中央の原点値を定義しないので、これらの「Thinning カーネル」は 形状の Thinning だけでなく、形状の Thicken にも使えて、SKIZ(影響領域)を生成できます。注意深く見れば、4連結カーネルのそれぞれが、実際には8連結セット内にネガかつ180度回転された形でも存在し、その逆もあることに気づくでしょう。例えば 'ThinSE:41' と 'ThinSE:84' は、互いにネガ化された回転です。その理由は、4連結性と8連結性が、(ネガ化された画像を使う)細線化と太線化のモルフォロジー手法の双対性を介して、互いに密接に関連しているからです。本質的に、4連結を保つ「Thinning カーネル」を画像の太線化に使うと、形状の周りに8連結の背景スケルトン(刈り込まれていない SKIZ)が生じ、その逆もあります。したがって、ネガ化された形(つまり thicken と thin の手法が入れ替わる)を使うことで、同じ演算に対してもう一方の形の連結性を生成できます。
距離勾配モルフォロジー
'Distance' モルフォロジー手法は、可能な多くの特殊手法の最初のものです。これが行うのは、特殊なカーネルを使って、各前景ピクセルの形状の「縁」からの距離を測定することです。より具体的には、ピクセルの「ゼロ」または「黒」の色値からの距離を測定します。ただし、これは純粋な二値(黒地に白)の形状でのみ機能します。とはいえ後で見るように、アンチエイリアスのかかった形状を距離手法で扱えるよう変更できます。そして、特別に設計された 距離カーネル でのみ機能します。距離カーネルは画像に適用され、各ピクセルに最小のピクセル値プラスその距離に対するカーネル値が割り当てられます。これは、前のモルフォロジー手法で見たような複数回の反復を必要としないアルゴリズムを使って、画像全体に同時に適用されます。このため、単一の原始モルフォロジー演算とほぼ同じくらい速く、これは例えば Thinning スケルトン モルフォロジー手法と比べると目もくらむほど高速です。画像全体に適用されるため、'iteration ' 引数は不要です。同じカーネル演算を繰り返す(反復する)と、結果にそれ以上の変化が生じないからです。 |
IM v6.6.9-4 以前は、カーネルが通常の Erode に似た技法を使って適用されたため、'-1' の反復回数が必要でした。これはもう必要なく、ゼロ(無動作)以外の与えられた「iteration」引数は、現在では単に無視されます。 |
|---|---|
以下は、'Distance' 手法を「人物」形状に使った例です。 |
magick man.gif -threshold 50% \
-morphology Distance Euclidean:4 \
+depth distance.png
さて、これは実にエキサイティングでした、なんてことはありません! 問題は、最終画像の色が非常に非常に暗いことです。しかし、良いモニターをお持ちで近くで見れば、「人物」がいた所に非常に暗い「幽霊」のような形状が見えるかもしれません。何が起きたかというと、少なくともこの小さな画像では、すべてのピクセルが縁に「近く」、あまり大きな「距離」値が割り当てられないのです。 | _'Distance' 手法を使う場合は、PNG 画像が推奨されます。それは、JPEG のような色損失なしに、例えば GIF よりも大きな出力値の「深度」を提供できるからです。
これは、8ビット GIF ソース画像を読み込んだにもかかわらず、出力が(私の Q16 版 IM に対して)16ビット深度にリセットされるよう、深度設定 "+depth" が使われた理由でもあります。
Q8 版 IM のユーザーには、(下記の次のセクションで)使われる「スケーリング値」を調整するために、距離カーネル(下記)の 'scale' 距離カーネルオプションについて読むことをお勧めします。Q8 版 IM を、この ユークリッド距離カーネル のような非整数距離カーネルとともに使うのは推奨されませんが、より精度の低い結果を生みます。
これら2つの側面をよりよく理解するには、IM 例の 品質と深度 のセクションを参照してください。
---|---
デフォルトでは、組み込みの 距離カーネル は各ピクセルに「100 × {_ピクセル距離}」の色値を適用します。あるピクセルがこれより明るい場合、最小の距離が割り当てられるよう、その値に設定されます。その結果、形状の最も縁にあるピクセルには背景色より100単位多い値が割り当てられます。さらに内側の次のピクセルには100単位多く与えられます。正確に何単位割り当てられるかは、使われる 距離カーネル によって与えられます。そこで、上の画像で設定された最大の色値を見てみましょう。
magick identify -verbose distance.png | grep max:
すなわち、結果画像の最大の色値は '1616' で、画像内で最も「明るい」ピクセルを非常に暗い 2.5% グレーにし、最も近い縁からの距離は 16.16 ピクセルです。言い換えれば、非常に暗いが本当に完全に黒ではない画像が見えます。 数学的な "[-auto-level](https://imagemagick.org/command-line-options/#auto-level)" を使って、結果の色値を、最も明るい、すなわち縁から最も遠いピクセルが白に設定されるよう調整してみましょう。こうすれば、生成された「距離勾配」の完全な効果を実際に見られます。 |
magick distance.png -auto-level distance_man.gif
| この画像に対して生成された正確な「距離」値はもう気にせず、距離の見える効果だけが必要なので、画像を GIF 画像ファイル形式で保存・表示できます。
---|---
![[IM Output]](../static/img/morphology/distance_man.gif)
これが 'Distance' 手法が行うことです。使われた特定の 距離カーネル に従って、各ピクセルが最も近い縁からどれだけ遠いかを定義する勾配を、与えられた形状全体に生成します。 結果の「距離」画像を明るくするもう一つの方法は、実際にはより大きな距離カーネルの 'scale ' 値を使うことです。例えば 3000 単位の値です(Q8 ユーザーはおそらく 20 の値を使えます)。 |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean:4,3000 distance_scaled.gif
![[IM Output]](../static/img/morphology/distance_scaled.gif)
距離勾配は黒から白までを覆わず、何らかのグレースケール値でピークに達したことに注意してください。ピークの「距離」をすでに知っているので、その最大ピークは 16.16 * 3000 => 48480、つまり約 74% グレーと計算できます。パーセンテージのスケーリング係数も使えます。例えば、縁からのピクセル距離ごとに 8% の色範囲値を使います。 |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean:4,8% distance_scale_percent.gif
![[IM Output]](../static/img/morphology/distance_scale_percent.gif)
ご覧の通り、今回は最大距離に達する前に最大値の限界に達しました。距離勾配が覆える最大距離は (最大範囲で 100%) / (1ピクセルあたり 8%) => 12.5 ピクセル距離 と計算できます。もちろん、HDRI 版の ImageMagick を使っていれば、完全な距離値はメモリ内に保持されます。少なくとも、その値を Clamp するか、非浮動小数点の画像ファイル形式に保存するまでは。特殊な 距離スケーリングフラグ '!' を使えば、関心のある最大ピクセル距離を直接指定することもできます。 形状の縁からの最大距離が 16.16 であることをすでに知っているので、少なくとも 18 ピクセルの限界を要求します。 |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean:4,'18!' distance_range.gif
![[IM Output]](../static/img/morphology/distance_range.gif)
'!' フラグは、色範囲の限界に達する前に 'n' 個のグレースケール値が得られるよう距離をスケールします。したがって、1 の値は、画像の縁の直接の近隣であるピクセルだけを「フェザリング」(つまりグレー化)します。ご覧の通り、すべてのスケーリング手法は、'Distance' 手法を実行している形状の実際のサイズに大きく依存します。小さすぎると非常に暗くなり、あなたのニーズに対して正確でないかもしれません。大きすぎると、距離が ImageMagick の コンパイル時の品質 の最大可能色値によって「クリップ」されるかもしれません。カーネルの 'scale ' 係数の詳細については、下記の 距離カーネル のセクションを参照してください。これらの例で使った人型の「形状」について、最後に1つ注記しておきます。この形状は単一ピクセルの「穴」を含んでおり、その周りに一種の「勾配の井戸」を作りました。これは結果の「距離勾配」画像の上半分に非常に強い効果を生じます。これに対する一つの解決策は、'[Close](#close)' を使ってその穴を除去し、形状を「きれいで滑らか」にすることです。例えば…
magick man.gif -morphology Close Diamond man_clean.gif
magick man_clean.gif -morphology Distance Euclidean \
-auto-level distance_clean.gif
もちろん、これは形状の「脚」の間の隙間も「閉じる」効果があり、最終結果の下半分に影響します。代替の解決策は、フラッドフィル 手法を使って画像の外側を抽出し、それを新しいマスクに変換することです。その結果、どの穴も閉じられますが、画像の外側の境界は保たれます。例えば…
magick man.gif -gamma 0,1,1 -bordercolor black -border 1x1 \
-fill red -floodfill +0+0 black -shave 1x1 \
-channel R -separate +channel -negate man_floodfill.gif
magick man_floodfill.gif -morphology Distance Euclidean \
-auto-level distance_floodfill.gif
距離カーネル
与えられるカーネルは非常に特殊です。各ピクセルに割り当てられる実際の距離測定を定義するために使われるからです。例えば、以下は組み込みの「距離カーネル」の1つの カーネル表示 出力です。
magick xc: -define morphology:showkernel=1 -precision 3 \
-morphology Distance:0 Chebyshev:3 null:
注意すべき重要な点は、「原点」(この場合はカーネルの正確な中央)がゼロの値を持つことです。これは非常に重要です。その「原点」は、より大きな値で囲まれており、これは「原点」からの距離が大きくなるにつれて線形に増加します。カーネルがこの特定の方法で定義されていない場合、予期しない奇妙な効果が生じることがあります。カーネルで与えられる値は、ピクセルにすでに「既知」として割り当てられた距離に加算される実際の「値」です。ただし、その値がすでに割り当てられているものより小さい場合に限ります。その結果、「白い」ピクセルは縁に近いほど暗くなり、縁から遠いほど(以前に割り当てられた値に加算して)線形に明るくなります。提供されているすべての組み込み距離カーネルは、2つの省略可能な k_arguments を取れます…
{_distance_kernel_}[:{_radius_}[,{_scale_}[%][!]]]
最初の引数は、すべての 形状カーネル と同様、カーネルの radius で、生成するカーネルの大きさを定義します。デフォルトでは、組み込み距離カーネルの radius は '1' に設定され、非常に小さな 3×3 のカーネルになります。これはほとんどの場合かなりうまく機能します。2番目の引数 'scale ' は、1ピクセル長の距離を表すのに使われる距離スケールを設定します。上の例で示した通り、デフォルト値は '100' です。すなわち、例えば '300' という最終的なピクセル値またはグレースケール値が与えられたピクセルは、縁からちょうど「3ピクセル」離れているはずです。 距離スケーリング 前述の通り、より「正確」な距離測定のために「小数」距離を使えるよう、大きな 'scale ' 値が使われます。ただし、提供されている '[Euclidean](#euclidean)' 距離カーネルだけがそうした「小数」値を使います。前の例では、割り当てられた「最大距離」値は '1700' で、これは Q8 版の ImageMagick をオーバーフローさせます(品質、メモリ内のビット深度 を参照)。IM Q8 では、色値が最大 255(2^Q => 2^8 => 256 個の色値、0 から 255 の範囲)に達することしか許されません。したがって、'10' や '20' のような小さな scale を使うほうが、IM Q8 コンパイル時変種を使うユーザーにはよりうまく機能します。ただし、'[Euclidean](#euclidean)' カーネルとともに使うとはるかに精度が落ちます。このため、Q8 版 IM のユーザーは、他の「整数」距離カーネルを、スケール係数 '1' で使うよう自身を限定することが推奨されます。 スケーリング係数に '%' を含めることで、距離スケーリングを完全な色範囲のパーセンテージとして指定することもできます。つまり、色値範囲の '12.5%' のスケールを使うと、距離が色範囲の限界をオーバーフローする前に、約 8 ピクセルの距離メトリックを得られます。あるいは、代わりに '!' を使えます。これはスケールが色範囲の除数であることを意味します。すなわち、'20!' のスケールを指定すると、画像の縁から 20 ピクセルで色範囲の限界に達するよう距離スケーリングが設定されます。ただし、これらの「特殊スケーリングフラグ」を使っても、Q8 版 IM では依然として深刻な範囲精度の制限があります。多くの距離演算に必要なデータ値の範囲が単に足りないのです。もちろん、HDRI 版の IM に対しては、結果の色値も浮動小数点値として保存されるので、任意の scale(完全な浮動小数点を含む)を正確に使えます。ただし、そうした画像を非浮動小数点の画像ファイル形式に保存しようとする前に、色範囲を適切に再スケールするよう注意してください。
いくつもの異なる距離測定カーネルが提供されており、そのうちの1つは2通りの異なる方法で使えます。各カーネルは、縁からのピクセル距離を指定するための異なる「距離メトリック」を提供し、基本的に何を「最も近い縁」とみなすかを定義します。
Chebyshev(チェス盤)距離カーネル
'**Chebyshev**' 距離カーネルは最も単純で、「原点」周りのすべてのピクセルが、その近隣から単に1距離単位であることを指定します。すなわち、8つの近隣すべてが互いに「隣」です。したがって、直近の4近隣だけでなく、斜めの近隣もちょうど1単位離れています。これはしばしば、チェス盤上で「キング」または「クイーン」の駒が動くマス目の距離になぞらえられ、そのため一般に「チェス盤(Chessboard) 」距離メトリックとも呼ばれます。ただし、距離カーネルは1ピクセルの距離あたり 100 距離単位というデフォルトの {scale} 係数を使うことに注意してください。したがって、原点から1歩離れるごとに距離は 100 単位になります。これは前の上記の例で使われたカーネルでもありました。以下は、それが生成する実際のカーネルです…
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Chebyshev null:
このカーネルの名前は、この形の距離測定を最初に数学的に記述したロシアの数学者パフヌティ・チェビシェフのものです。この測度については Wikipedia, Chebyshev Distance で詳しく知ることができます。'Chebyshev' 距離測度を使うと、ピクセルの最終距離は、最も近い縁までの X または Y の最大値になります。ただし、斜め距離がわずか1単位なので、画像内の最大距離は通常、予想より小さくなります。このカーネルの「メトリック」を使って「距離勾配」を生成してみましょう。ただし、何が起きているかを見られるよう、無限反復回数を使った、より遅い 'Iterative Distance' モルフォロジー手法を使いましょう。 | |
magick man.gif -threshold 50% +depth \
-define debug=true -morphology IterativeDistance:-1 Chebyshev \
chebyshev_gradient.png
magick identify -format 'Maximum Distance = %[max]' chebyshev_gradient.png
magick chebyshev_gradient.png -auto-level chebyshev_gradient.gif
rm chebyshev_gradient.png
| _'Iterative Distance' モルフォロジー手法は、値に変化が見られなくなるまで距離カーネルを繰り返し適用することで距離を計算します。
これは、画像全体に距離を設定するのに2パス手法を使う、より通常の 'Distance' 手法よりずっと遅いです。ただし、'Distance' 手法の詳細出力はずっと面白くありません。
---|---
詳細 フラグをオンにして、コマンドが画像を通る各反復(パス)中に、演算によって何ピクセル(すべて白ピクセル)が変化したかを出力するようにしました。次に、結果を調整(正規化)して結果の勾配が見える画像にする前に、生成された「最大」距離('1400')を抽出しました。最大距離 '1400' は、画像内で最も明るいピクセル(実際にはそうした4ピクセルのクラスター)の値です。この情報は、この距離カーネル(メトリック)の最も重要な結果です。それは、この形状内に収まる最大の正方形のサイズを表すからです。具体的には、それら4つの最大ピクセルを中心とする、14ピクセルの半径、または1辺約 (R-1)*2+1 => 27 ピクセルの正方形です。このカーネルの距離単位はすべて常に '100' の倍数なので、この最終的な距離値は常に '100' の倍数になり、小数成分を持つことは決してありません。基本的に、このカーネルは整数距離を生成するので、このカーネルでは距離情報を失うことなく単純な「1単位」の _scale を使えます。これは、Q8 版の ImageMagick を使っている場合や、これを非常に非常に大きな画像に適用する場合に推奨されます。 以下は、生成された距離勾配の特徴を強調する、形状の「脚」の間の勾配の拡大です。 |
magick chebyshev_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% chebyshev_magnify.gif
![[IM Output]](../static/img/morphology/chebyshev_magnify.gif)
ご覧の通り、'[Chebyshev](#chebyshev)' 距離カーネルは非常に正方形のような勾配を生成します。これはこの単純な形の距離メトリックの具体的な特徴で、距離カーネル自体の正方形の性質を直接反映しています。上記は、画像の上部近くの図の「腹」にある4つの最大距離ピクセルも示しています。これら4点のいずれかに正方形を中心配置することで、図の中に完全に含まれる最大の奇数サイズの正方形を生成できます。ただし、そうした「ピーク」がいくつもありうることに注意してください。
Manhattan(タクシー)距離カーネル
'**Manhattan**' 距離カーネルは、最も近い縁までの X と Y の値を加算することで距離を測定します。これは基本的に、ニューヨークのマンハッタンのような大都市の街路を走るタクシーのように、格子状の移動だけに制限されたときに移動する必要のある距離です。このため、この測度のより一般的な別名は「タクシー(Taxi Cab) 」または「市街地区画(City Block) 」距離メトリックです。Wikipedia, Manhattan Distance で詳しく知ることができます。以下は、それが生成する実際のカーネルです…
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Manhattan null:
斜めが今や中央から '200'、すなわち2単位の値を持つことに注意してください。すなわち、斜めのピクセルに到達するには、前述の格子状移動で2ピクセルを通過しなければなりません。この結果、斜めは予想より大きくなる傾向があり、したがって最終的な距離測定も大きくなる傾向があります。このメトリックを使って、再び最大距離と「距離勾配」画像を抽出してみましょう。 | |
magick man.gif -threshold 50% +depth \
-morphology Distance Manhattan manhattan_gradient.png
magick identify -format 'Maximum Distance = %[max]' manhattan_gradient.png
magick manhattan_gradient.png -auto-level manhattan_gradient.gif
rm manhattan_gradient.png
今回は 'Iterative Distance' を使いませんでしたが、たとえ使ったとしても、変化したピクセルの総カウントは正確にはなりません。前の '[Chebyshev](#chebyshev)' カーネルだけが、ピクセル距離を一度だけ設定します。画像の最終的な最大距離が '1700' 距離単位とずっと大きくなり、形状内の最大ピクセルを縁から 17 ピクセルにしていることに注目してください。この距離カーネルも「整数」カーネルなので、情報を失うことなく scale をわずか「1単位」に設定できます。 以下は勾配の拡大です。 |
magick manhattan_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% manhattan_magnify.gif
![[IM Output]](../static/img/morphology/manhattan_magnify.gif)
ご覧の通り、'[Manhattan](#manhattan)' 距離カーネルはダイヤモンドのような勾配を生成しました。これは基本的に、実際のカーネル値が反映する通り、この単純な距離メトリックが表すものです。
Octagonal(八角形)距離カーネル
'**Octagonal**' 距離カーネルは、他の2つとは少し異なります。これは、最も縁にあるピクセルにはまず Manhattan 距離を生成し、次に縁から2単位距離のピクセルには Chebyshev を使うことで作られます。それから、3単位距離のピクセルには再び Manhattan 距離を使う、という具合に繰り返します。その結果、2つの単純なカーネルを使うことから生じる距離の「インターリーブ」または「平均化」が得られます。このカーネルは2つの整数距離カーネルのインターリーブに基づくので、これも整数距離カーネルです。したがって、より低品質の ImageMagick 版や、非常に大きな距離測定のために、'1単位' の scale を使ってより小さな値を生成できます。距離の形状も2つのカーネルの混合で、'[Octagon](#octagon)' 形状のカーネルに相当するものを生成します。以下は、それが生成する実際のカーネルです…
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Octagonal null:
カーネルが最小かつデフォルトのサイズ半径 2 を持ち、5x5 ピクセルのカーネルを形成することに注意してください。この少し大きなカーネルは、カーネルの「インターリーブ」を生成するために必要です。全体の距離は一般に、真の距離よりわずかに小さくなります。ここで再び最大距離を計算します… | |
magick man.gif -threshold 50% +depth \
-morphology Distance Octagonal octagonal_gradient.png
magick identify -format 'Maximum Distance = %[max]' octagonal_gradient.png
magick octagonal_gradient.png -auto-level octagonal_gradient.gif
rm octagonal_gradient.png
'1500' という結果は「整数」距離で、実際には小さすぎる Chebyshev 距離と、大きすぎる Manhattan 距離の間に入ります。ただし一般に、「整数」値を保ちながら、形状の中心までの実際の真の距離に妥当なほど近いはずです。 以下は勾配の拡大です。 |
magick octagonal_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% octagonal_magnify.gif
![[IM Output]](../static/img/morphology/octagonal_magnify.gif)
脚の隙間の上部の周りに発達した「八角形」の距離がはっきり見えます。斜めが、太い斜め線と細い斜め線のインターリーブを使って生成されたことも見えます。
分数八角形(Fractional Octogon)距離カーネル
名前付きの距離カーネルは提供されていません。しかし、これは私たちが研究している距離カーネルの並びにこの時点でうまく収まります。 八角形の形状を使って、別の種類の整数距離カーネルを生成できます。ただし、この場合の整数距離は1ピクセルあたり2の単位値を使うので、実際には生成される距離値を半分にする必要があり、生成される小さな整数から分数値を生成します。これが「分数八角形(Fractional Octogon)」という名前の由来です。これを行うには、近隣のピクセル間の整数距離 2 と、斜めには 3 を使います。
'3: 3,2,3
2,0,2
3,2,3'
「半整数」が生成されうるので、使える最小の最小スケールは '2単位' です。そして「ナイトの動き」ほど正確ではありませんが、うまく機能します。このカーネルの八角形は、前のカーネルが生成する「平面」ではなく、直交方向に「点」を持ちます。これは次の「ナイトの動き」カーネルに関連しますが、まったく同じではなく、'knights' カーネルの一種の「ほぼ整数」形と考えられます。これを前の IM 距離カーネルと同じようにスケールしたい場合は、このカーネルを使えます。
'3: 150,100,150
100, 0 ,100
150,100,150'
以下は例です | |
magick man.gif -threshold 50% +depth \
-morphology Distance '3:3,2,3 2,0,2 3,2,3' \
fractional_gradient.png
magick identify -format 'Maximum Distance = %[max]' fractional_gradient.png
magick fractional_gradient.png -auto-level fractional_gradient.gif
rm fractional_gradient.png
'34' という結果は「整数」距離ですが、実際の最大距離 17 を生成するには2で割る必要があります。ただし、これも整数ですが、16.5 のような分数距離として出てくることも同じくらい容易にありえます。距離結果のこの分数的側面が、ほとんどのカーネルが 100 の単位を使って定義される理由であり、純粋に整数の距離カーネルから離れていく後のカーネルでは、よりいっそう一般的になります。 以下は勾配の拡大です。 |
magick fractional_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% fractional_magnify.gif
![[IM Output]](../static/img/morphology/fractional_magnify.gif)
勾配は(結果を注意深く調べれば)八角形の形状です。ただし、共通の距離値を持つピクセルを見るのは難しいです。形状をよりはっきり見るために、上の画像を取り、同じ色値を持つピクセルの集合を1つ赤で着色しました。 |
magick fractional_magnify.gif -fill red -opaque gray53 \
fractional_magnify_shape.gif
![[IM Output]](../static/img/morphology/fractional_magnify_shape.gif)
ご覧の通り、同じ値のピクセルは一般に「ナイトの動き」のパターンで離れていますが、八角形の形状を生成する線を形成します。ただし、八角形は 八角形距離カーネル から45度回転しています。この形状の違いが、最終的な最大距離を大きくする原因でもあります。本質的に、このように回転されたより大きな八角形が形状によりよく収まるので、より大きな最大距離の結果になります。使えるもう1つの分数距離「整数」カーネルが以下ですが、距離は 2 ではなく 3 単位で表されます。すなわち、結果を3で割ってピクセルサイズで表した距離を得る必要があり、これは使う除数としてあまり良くありません。とはいえ、これも別の八角形型の距離メトリックです。
'3: 4,3,4
3,0,3
4,3,4'
下記の ユークリッド(ナイトの動き)距離カーネル も八角形スタイルの形状を生成しますが(すべての 3x3 距離カーネルがそうです)、斜めに沿ってできるだけ正確であろうとします。これは最良の考えかどうかわかりませんが、この種の中で最も数学的に論理的な八角形距離カーネルです。
Chamfer 距離カーネル
| 名前付きの距離カーネルは提供されていません。しかし、これは私たちが研究している距離カーネルの並びにこの時点でうまく収まります。 'Chamfer' 距離カーネル(未実装)は、距離行列を埋めるのに使われる数値(典型的には整数)だけを使って定義されます。例えば、上で説明した任意の 3x3「八角形」型距離カーネルを定義するために、2つの数値を与えられます。以下は、前の整数カーネルの定義です。 Chebyshev | Chamfer:1,1 |
|---|---|
| Manhattan | Chamfer:1,2 |
| 分数八角形(Fractional Octagon) | Chamfer:2,3 |
| 分数八角形 代替(Fractional Octagon Alturnative) | Chamfer:3,4 |
| --- | --- |
| これらのカーネルはすべて単純な半径 1 のカーネルです。与えられる値は、それが使うべき実際の「距離スケーリング」値とみなせます。ただし、前の整数 Octagonal カーネルは、それを定義するのに3つの数値を持つ半径 2 の Chamfer カーネルを必要とすることに注意してください。最もよく知られた Chamfer カーネルは半径 2 のカーネル 'Chamfer:5,7,11' で、これは非常に正確な距離を生成し、また整数距離値を生成するので Q8 ユーザーによく適しています。伝統的に、このカーネル(chamfer 5,7,11)は次の形です… |
'5: - 11 - 11 -
11 7 5 7 11
- 5 0 5 -
11 7 5 7 11
- 11 - 11 -'
または上記を 20 倍して、ImageMagick が距離カーネルに通常使う同じピクセル距離スケーリング(100)を生成します…
'5: - 220 - 220 -
220 140 100 140 220
- 100 0 100 -
220 140 100 140 220
- 220 - 220 -'
| このカーネルが実際にはカーネルのすべての距離を埋めていないことに注目してください。それは、それらの値が、すでに提供されている他の値から距離を得るからです。すなわち、距離カーネルを完全に定義するために2次元配列全体を実際に埋め尽くす必要はありません。とはいえ、処理を容易にするため典型的にはそうされます。以下は、私が研究で見つけた他の既知の Chamfer カーネル(整数値のみを使用)のリストです。 Chamfer:3,4 | /3 |
|---|---|
| Chamfer:5,7,11 | /5 |
| Chamfer:99,141,221 | /100 |
| Chamfer:987,1414,2206 | /1000 |
| Chamfer:12,17,27,38,43 | /12 |
![[diagram]](../static/img/img_diagrams/chamfor_values.png)
半径 3 の Chamfer カーネルを定義するために5つの値がどう配置されるか
上の表で強調されているカーネルは、最もよく知られ最も一般的に使われる Chamfer 距離カーネルです。これは小さな整数値だけを使って画像を生成するので、大きな距離勾配画像を精度を失わずに保存できます。また、少なくとも想像できるほぼあらゆる目的に対して十分なほど、非常に正確です。おまけに、距離結果は循環小数を作らず、正規化されたときに1桁の分数を作るだけです。これは、多くの画像処理パッケージでよく選ばれるもう一つの理由です。"Chamfer:5,7,11" はデフォルトの "Chamfer" 距離カーネルとみなすべきです。
Euclidean(ナイトの動き)距離カーネル
'**Euclidean**' カーネルは、正確な浮動小数点の距離数を使って生成されます。ただし、これを非 HDRI 版の ImageMagick で機能させるには、分数の斜め距離の使用が必要です。例えば、2の平方根、約 1.4142 距離単位の値を持つ斜めです。これを機能させるため、距離は(上記すべてのカーネルと同様)100 の値でスケールされ、分数のパーセンテージ距離を生成します。以下は、それが生成するデフォルトのカーネルです…
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Euclidean null:
さて、デフォルトの radius 1 を使うと、精度の面では前のカーネルに対する大きな改善ですが、依然としていくつかの制限があります。基本的に、これは45度の斜めと直交(X と Y)の動きだけで距離を提供します。すなわち、距離はチェスのゲームにおける「ナイトの動き 」にやや似ています。以下は、デフォルトの 'Euclidean' すなわち「ナイトの動き」カーネルを使って作られた最大距離と「距離勾配」画像です。 | |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean knight_gradient.png
magick identify -format 'Maximum Distance = %[max]' knight_gradient.png
magick knight_gradient.png -auto-level knight_gradient.gif
rm knight_gradient.png
ご覧の通り、この特定の形状に対しても '1700' 距離単位の距離値が得られます。通常、結果は、より小さな '[Chebyshev](#chebyshev)' 距離と、より大きな '[Manhattan](#manhattan)' 距離の間の何らかの分数距離になります。それが単純な '100' の倍数として出てきて、しかもたまたま '[Manhattan](#manhattan)' 距離と同じになったのは、まったくの幸運です。ピクセルまでの実際の距離は、実際には斜め距離と直交(軸)距離の和です。すなわち、完全なユークリッド距離ではありませんが、可能な限り最小の距離カーネル(半径 1)を使いながら得られる、それに最も近いものです。 以下は、形状の「脚」の間の勾配の拡大です。 |
magick knight_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% knight_magnify.gif
![[IM Output]](../static/img/morphology/knight_magnify.gif)
ご覧の通り、勾配は、「整数」距離カーネルで得られるような玉ねぎ状の「段差」や「階段」効果を伴わず、ずっと丸みのある見た目になっています。これは、各ピクセルが縁から個別の分数距離を割り当てられる可能性が高いからです。ただし、勾配の最終的な形状は実際にはおおよそ「八角形」の形状で、前の '[Octagonal](#octagonal)' 距離カーネルで得られるような上下の平面ではなく、コンパスのような点を持ちます。一般的な距離作業(「フェザリング」など)には、このデフォルトの 'Euclidean' すなわち「ナイトの動き」カーネルが良い結果を提供します。ただし、「整数」距離が得られないので、これを '1' の距離スケール係数で使うことはできず、Q8 版の ImageMagick にはあまり有用でありません。
より大きなユークリッド距離カーネル
生成される 'Euclidean' カーネルの 'radius ' を増やすと、よりいっそう正確な「ピタゴラス」すなわち真の「ユークリッド」距離メトリックを生成します。radius が大きいほど結果は正確になりますが、モルフォロジー '[Distance](#distance)' 手法の実行に時間がかかります。ただし、必要な反復回数は少なくなります。しかし、半径 4 を超えるとそれほど精度は上がらず、速度の損失だけがずっと大きくなります。非常に大きな 'Euclidean' カーネルを使って精度を改善する例については、下記の アンチエイリアス形状での距離 を参照してください。以下は、推奨される半径 4 を使った真の 'Euclidean' カーネルで、より大きな 9×9 のカーネルを生成します…
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Euclidean:4 null:
半径 4 を使う追加の利点は、カーネルが辺の長さ 3,4,5、またはデフォルトのカーネルスケールで 300,400,500 単位を持つピタゴラスの三角形も含むことです。これは結果画像の分数成分の数を減らせますが、本当にわずかな効果にすぎません。とはいえ、より高い精度のための論理的な選択ではあります。以下はその適用です… | |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean:4 euclidean_gradient.png
magick identify -format 'Maximum Distance = %[max]' euclidean_gradient.png
magick euclidean_gradient.png -auto-level euclidean_gradient.gif
rm euclidean_gradient.png
このより大きな半径の 'Euclidean' カーネルを使った結果、最終的な最大距離はこれまでで最も正確な最大距離測定になります。また、形状が非常に規則的でない限り、画像内に複数の「最も明るい」ピクセルが得られる可能性を低くします。 以下は、形状の「脚」の間の勾配の拡大です。 |
magick euclidean_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% euclidean_magnify.gif
![[IM Output]](../static/img/morphology/euclidean_magnify.gif)
ご覧の通り、勾配は「脚の隙間」の端の周りにほぼ完璧な円形の勾配を生成します。このカーネルを使うコストは、前述の通り、わずかに遅い実行時間です。
距離カーネルの比較
ここで再び、拡大画像を並べて比較します。これは、使われた4つの距離メトリックそれぞれが生成する非常に異なる勾配を明確に示しています。
![[IM Output]](../static/img/morphology/chebyshev_magnify.gif)
Chebyshev
(チェス盤) | ![[IM Output]](../static/img/morphology/manhattan_magnify.gif)
Manhattan
(タクシー) | ![[IM Output]](../static/img/morphology/octagonal_magnify.gif)
Octagonal
(混合) | ![[IM Output]](../static/img/morphology/knight_magnify.gif)
Euclidean
(ナイトの動き) | ![[IM Output]](../static/img/morphology/euclidean_magnify.gif)
Euclidean
(radius=4)
---|---|---|---|---
以下は別の比較で、今回はピクセルを拡大せずに、左下の角に近い単一の黒ピクセルからの距離を取得しています。
magick -size 100x100 xc: -draw 'point 20,80' distance_start.png
for kernel in chebyshev manhattan octagonal euclidean euclidean:2 euclidean:4
do
magick distance_start.png -morphology Distance $kernel \
-auto-level point_$kernel.png
done
![[IM Output]](../static/img/morphology/point_chebyshev.png)
Chebyshev
(チェス盤) | ![[IM Output]](../static/img/morphology/point_manhattan.png)
Manhattan
(タクシー) | ![[IM Output]](../static/img/morphology/point_octagonal.png)
Octagonal
(混合) | ![[IM Output]](../static/img/morphology/point_euclidean.png)
Euclidean
(ナイトの動き) | ![[IM Output]](../static/img/morphology/point_euclidean_2.png)
Euclidean
(radius=2) | ![[IM Output]](../static/img/morphology/point_euclidean_4.png)
Euclidean
(radius=4)
---|---|---|---|---|---
これらの画像は、ピクセルがおそらく予想するよりも開始ピクセルに近いと考えられる線を明確に示し、各種カーネルがより複雑になるにつれてどう滑らかになるかを示します。最後のものだけが「より近いピクセル」の見える線を示しませんが、半径 4 でさえそれらは存在します。このカーネルでは、線はソースから非常に遠い距離でしか現れません。半径 7 はさらに良い結果を生みますが、速度の大きな犠牲を伴います。とはいえ、結果画像のアーティファクトを避けるために、ときにはそうした精度が必要です。最初の3つのカーネルだけが整数距離を生成し、(適切なスケール調整を伴って、個々のカーネルの説明を参照)Q8 版の ImageMagick で使えることを思い出してください。そして、多くの画像処理パッケージが典型的に使うのは、これらのものです。単一の点からの距離の計算は、後で下記の 制約付き距離 の例で再び見ます。
特殊なユーザー定義距離カーネル
提供された距離カーネルだけに限定されるわけではありません。「原点」でゼロ値を使い、それを取り囲む距離値を増加させるというルールを守る限り、他の非常に興味深い距離効果を生成できます。 例えば、ここでは、右側の任意のピクセルの値を大きくするだけの、非常に小さな ユーザー定義カーネル を適用します。 |
magick man.gif -threshold 50% +depth \
-morphology Distance '2x1+0+0: 0,100 ' \
-auto-level distance_linear.gif
![[IM Output]](../static/img/morphology/distance_linear.gif)
脚の間の隙間の効果に注目してください。それが生成する、ゆっくり増加する勾配を「リセット」します。 そしてここでは、両側だけから距離勾配を作りますが、各側に異なるスケールを使います! |
magick man.gif -threshold 50% +depth \
-morphology Distance '3x1: 50,0,100 ' \
-auto-level distance_sides.gif
![[IM Output]](../static/img/morphology/distance_sides.gif)
これらは、可能な距離カーネルの変種のいくつかの例にすぎません。他のものを思いつけるなら、ぜひ教えてください。画像の出来が悪い場合は、おそらくカーネルの原点設定が間違っているか、原点が「ゼロ」値でなかったのでしょう。これはモルフォロジー距離関数によってチェックされません。
アンチエイリアス形状での距離
Distance 手法は非常にうまく機能します。ただし、その機能の最良のテストは、距離関数を円に適用し、それを Shade して、関数が生成しうる最小の誤差さえ強調することです。
magick -size 129x129 xc: -draw 'circle 64,64 60,4' \
-negate circle_shape.png
magick circle_shape.png -morphology Distance Euclidean:4 \
-auto-level cone_distance.png
magick cone_distance.png -shade 135x30 -auto-level \
+level 10,90% cone_distance_shade.png
上記から、「円錐」のような結果は得られるものの、滑らかな「円錐」にはほど遠いことがわかります。縁から始まる放射状の稜線のネットワークで覆われています。シェードされた円錐の縁を注意深く見ると、円錐が元の形状の滑らかな円形の底面を持たないことがわかります。それは高度に「エイリアス」または「階段状」になっており、これらの「段差」が距離関数によって反映されて、見える放射状の稜線を形成しています。問題は、距離手法が、円が滑らかな見た目を出すために縁の周りで使う小さな「グレー」のアンチエイリアスピクセルについて何も知らないことです。実際、どの「グレー」ピクセルも一般に、それが表すアンチエイリアスの部分的な縁ピクセルではなく、丸ごと1ピクセルとして考えられています。 必要なのは、それらのグレーの縁の値を何らかの形で結果に含めることであり、これは距離手法が適用される前の前処理ステップを使って行われます。 |
magick circle_shape.png -gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance Euclidean:4 -auto-level \
-shade 135x30 -auto-level +level 10,90% cone_antialiased.png
![[IM Output]](../static/img/morphology/cone_antialiased.png)
行ったことは、まずそれらすべてのグレーピクセルを、そのピクセルが円の境界の内側にどれだけ入っていたかの表現から、そのピクセルが円の縁からどれだけ離れていたかの表現へと変換することでした。結果からわかるように、階段状の縁の誤差はほとんどすべて修正されました。残る見える誤差は縁から離れた所でのみ生じ、「円錐」の中心に向かうほどより見えるようになります。これらは、4ピクセル(ユークリッド距離カーネルのサイズ)ごとに距離関数を何度も反復することで引き起こされます。したがって、縁から遠ざかるにつれて、わずかな不正確さが強調されます。 これはほとんどの状況では問題になりませんが、より大きなユークリッドカーネルを使ってよりずっと正確で滑らかな結果を生成することで、それらの小さな誤差さえ低減、おそらく排除さえできます。ただし、これは生成により時間がかかります。 |
magick circle_shape.png -gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance Euclidean:7 -auto-level \
-shade 135x30 -auto-level +level 10,90% cone_improved.png
![[IM Output]](../static/img/morphology/cone_improved.png)
結果は、アンチエイリアスまたは滑らかな形状に対するほぼ完璧な距離関数です。以下は別の例で、今回は前のセクションの2つの ユーザー定義距離カーネル を使います。
magick circle_shape.png -gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance '2x1+0+0:0,100' -auto-level \
circle_gradient.png
magick circle_shape.png -gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance '3x1:50,0,100' -auto-level \
circle_ridge.png
アンチエイリアスピクセルの特別な処理がなければ、上記は画像全体にわたってこれほど滑らかな勾配を生成しないでしょう。とはいえ、依然として元の形状の「形」を復元する必要があります。
距離を用いた形状のフェザリング
上記の技法は、オブジェクトを適切に「フェザリング」するために形状のアルファチャンネルに適用できます。例えば、以下は形状のオブジェクトの周りの 10 ピクセルの「滑らかな」フェザーです。
magick rose_orig.png \
\( +clone -fill black -colorize 100% \
-fill white -draw 'circle 114,75 110,2' \
\) -alpha off -compose CopyOpacity -composite \
-trim +repage rose_shape.png
magick rose_shape.png \
\( +clone -alpha extract -virtual-pixel black \
-gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance Euclidean:4,10! \
-sigmoidal-contrast 3,0% \
\) -compose CopyOpacity -composite \
rose_feathered.png
これは、より単純な ぼかしフェザリング 技法とは異なり、すべての縁を厳密に保ちます。上記をアルファチャンネルに直接適用することもできましたが、一部の演算子は透明チャンネルを「アルファ値」ではなく「不透明度値」として扱います(具体的には "[-white-threshold](https://imagemagick.org/command-line-options/#white-threshold)" 演算子)。そのため、最終画像にマージし直す前に、グレースケール画像として扱うためにアルファチャンネルを抽出しました。特殊な距離カーネルは、形状の縁の近くに十分な距離勾配を生成するため、4ピクセルの ユークリッドカーネル を3回反復します。次に "[-level](https://imagemagick.org/command-line-options/#level)" 演算子が、それを縁('0')から形状内に 10 ピクセル('1000' 単位)までの線形勾配の形に変換します。"[-virtual-pixel](https://imagemagick.org/command-line-options/#virtual-pixel)" 設定も提供され、矩形の画像コンテナの縁に触れる形状も、透明に囲まれていると考えられるようにします。この場合の距離関数の結果は「線形ランプ」または「ベベル」で、鋭い見た目の効果を生成できます。そのため、小さな "[-sigmoidal-contrast](https://imagemagick.org/command-line-options/#sigmoidal-contrast)" 修正が、透明から不透明へのこの遷移を滑らかにします。強度(上記では '3')が高いほど、縁でのフェザリングが鋭くなります。フェザーを透明へとより滑らかに「先細り」させたい場合は、上記コードの '0%' を '50%' に置き換えて、シグモイド曲線の「肩」を 10 ピクセルのフェザーの中央に配置します。 ビットマップ形状のフェザリング 形状が、GIF 画像や 画像マスク からのものなどビットマップである場合、上記のフェザリング演算を単純化できます。例えば…
magick figure.gif -channel A -virtual-pixel transparent \
-morphology Distance Euclidean:4,3! boolean_feathered.png
主な変更点は、距離関数の前処理・後処理がはるかに少なく済み、'3!' を使って特殊な距離単位を単純に指定できることです。これは形状の縁の周りに3ピクセルの線形勾配を生成します。この種のフェザリング(より大きな「線形フェザー」)の別の例は、サムネイル、ソフトエッジ で見られます。上記で "[-sigmoidal-contrast](https://imagemagick.org/command-line-options/#sigmoidal-contrast)" 演算子を使ってより大きなフェザーを滑らかにできますが、現時点ではこれが透明度をアルファ値ではなく「マット」値として処理することに注意してください。そのため、前の解決策の '05' の代わりに '100%' の値を使うべきです。ビットマップ形状の場合は、上記のより複雑な距離フェザーをその結果に適用する前に、アルファチャンネルを少し滑らかにするために "-blur 1x0.7" を適用するほうが良いかもしれません。
条件付き(制約付き)モルフォロジー
ここでは、繰り返しのモルフォロジー演算を、画像の特定の領域に制約または限定する技法を見ます。基本的に、ある限界や関心領域を超えて「あふれる」または成長することがないようにするために使える技法です。これは一般に何らかの「マスク」画像を必要とし、典型的には、どのピクセルが更新されるかを限定する 書き込み保護マスク を使って行われます。
条件付き膨張
ご存じの通り、Dilate モルフォロジー手法 は、与えられたカーネル近傍に従って特定の形状を拡張します。「条件付き膨張(Conditional Dilation) 」は本質的に同じことですが、画像に繰り返し適用したときに膨張がどこまで広がれるかに限界を設定します。Draw フラッドフィル は、ある意味、究極の「条件付き膨張」です。それは、開始点と同じ色であるたまたまの任意の直交(Diamond カーネル 近傍)を単に塗りつぶします。例えば、いくつものディスクの1つの中で単一の点を選択し、そのディスクが完全に再着色されるまで条件付き膨張(フラッドフィル)して、他の形状から分離できます。
magick disks.gif -fill red -draw 'color 60,60 floodfill' \
cond_dilate_draw.gif
同様に、フラッドフィル演算子 を使って同じことができますが、開始点がユーザー提供の「条件色」にも一致する場合に限ります。 |
magick disks.gif \
-fill green -floodfill +10+40 white \
-fill blue -floodfill +30+50 white \
cond_dilate_floodfill.gif
![[IM Output]](../static/img/morphology/cond_dilate_floodfill.gif)
この場合、「緑」のフラッドフィルは「ディスクに当たり」(そして塗りつぶし)、一方「青」のフラッドフィル演算は一致しなかったので、ディスクは塗りつぶされませんでした。そうした「フラッドフィル」手法の問題は、ユーザー提供の単一の点からしか膨張できないことです。ただし、これは非常に高速で、画像全体に対して機能します。繰り返しまたは反復された Dilate はフラッドフィルと同じですが、複数の開始点を持てるものの、それが提供する塗りつぶし演算の限界や境界の理解がありません。その効果を限定するには、「開始点」だけでなく、塗りつぶしの「条件付き境界」も提供する必要があります。これを行うために、書き込み保護マスク(画像のどの部分が書き込み保護されるか)を作ります。
magick disks.gif disks.gif -morphology Erode:7 Diamond disks_big_center.gif
magick disks.gif -negate disks_mask.gif
magick disks_big_center.gif -write-mask disks_mask.gif \
-morphology Dilate:15 Diamond +write-mask disks_big_found.gif
上記ではまず Erode 手法 を使って、半径 7(直径 7x2+1 => 15 ピクセル)より大きい任意のディスクを位置特定しました。次に、発見された点を同じ量だけ「条件付き膨張」して、見つかったオブジェクトを「完璧に」復元します。「条件付き膨張」は、オブジェクトの復元に Open 手法 を使うのとは大きく異なることに注意してください。その手法は、元のオブジェクトの正確な形状ではなく、カーネルの内部の「ダイヤモンド」形状を生成したでしょう。また、「奇妙な形」や「中心からずれた」シード点も扱いません。反復回数 '15' は決定的ではありませんが、オブジェクトを完全に復元するのに十分大きくすべきです。 | _基本モルフォロジー演算 を実行するときは、大きな半径の Disk のようなはるかに大きなカーネルを使うより、Diamond や Square のような小さなカーネルを 反復回数 とともに使うほうが良いことを思い出してください。
これは 条件付き膨張 を行うときに特に重要になります。大きなカーネルは、事実上、複数のオブジェクトを隔てる隙間を「飛び越え」てしまうことがあるからです。
_
---|---
| _書き込みマスクとともに '-1' や(ほぼ)無限の反復回数を使わないでください。IMv7 のモルフォロジーは現在、ピクセルが書き込み不可であることを認識せず、形状の縁の周りに常に変化(これは決して書き込まれない)を見るので、画像にこれ以上変化が見られなくても中断しません。
これは IMv7 で修正される予定で、IMv7 は、書き込み保護されたピクセルに関して演算子を少し賢くする大きな内部再構築を提供しました。
_
---|---
以下は、書き込み保護マスク の汎用性の別の例です。画像を横切る斜め線が当たるディスクを見つけます。
magick -size 80x80 xc:black -fill white \
-draw 'line 0,0 79,79' disks_line.gif
magick disks_line.gif disks.gif \
-compose Multiply -composite disks_line_find.gif
magick disks_line_find.gif -write-mask disks_mask.gif \
-morphology Dilate:15 Diamond +write-mask disks_line_found.gif
追加のマスキングステップ(Multiply 合成 を使用)が、線が実際に 通る オブジェクトだけが「見つかる」ことを保証することに注意してください。このステップがなければ、線に近い、すなわち「シード点」のカーネル半径内にあるオブジェクト(上記では接しているだけ)も「膨張」してしまったでしょう。もちろん「近いオブジェクト」を含めたい場合は、最初のマスキングステップの前に、適切な半径のディスクカーネルで線を膨張させ(幅を広げ)てください。そうすれば、正方形カーネルの半径に頼るより、「近さ」をより制御できます。要約すると、条件付き膨張 は、より遅いものの、何を塗りつぶす、または「発見する」かを正確に選択するのにより汎用的な、多点 Draw フラッドフィル とみなせます。
制約付き距離
Distance モルフォロジー手法は、オブジェクト内の点が縁からどれだけ遠いかを見つけるのに容易に使えます。しかし、オブジェクト内の各点が別の点からどれだけ離れているかを見つけるのにも使えます。例えば、ここでは各点が単一の「シード」点からどれだけ離れているか(直線距離で)を発見します…
magick -size 100x100 xc: -draw 'point 20,80' distance_start.png
magick distance_start.png -morphology Distance Euclidean \
-auto-level distance_point.png
magick -font Casual -pointsize 140 label:D \
-trim +repage -gravity center -extent 100x100 \
-threshold 20% distance_bounds.png
magick distance_point.png \( distance_bounds.png -negate \) \
-compose multiply -composite -auto-level distance_direct.png
点からの距離は、私たちが関心を持つ唯一の点であるオブジェクトの形状でマスクされています。問題は、上記の距離勾配が「直線距離」、すなわち「開始点」までの直接距離を表すことです。その勾配を単にオブジェクトでマスクしても、それは変わりません。残念ながら、これは典型的に、ユーザーが「開始点からの距離」として望むものではありません。この形状が島を表すとすれば、この島の一方の端から他方へ「直接ルート」を取りたければ、かなり濡れてしまうでしょう。 本当に欲しいのは、「オブジェクト内の 」経路に限定された距離です。すなわち、距離をオブジェクト自体によって「制約」し、オブジェクト内で可能な限り最小の経路をたどってほしいのです。これを行うために、距離勾配が計算される際に「進入禁止」領域、すなわち画像の背景を横切る(に書き込む)ことがないよう、書き込み保護マスク を設定できます。
magick distance_start.png -write-mask distance_bounds.png \
-morphology IterativeDistance:150 Euclidean \
+write-mask -fill black -opaque white -auto-level \
distance_constrained.png
ご覧の通り、結果は正しい「画像を横切る距離」勾配で、開始点からの最大距離(白)が、オブジェクトの隙間を挟んで「目と鼻の先」にある島の反対端であることを明確に示しています。
通常の Distance 手法 ではなく、より低レベルの 'Iterative_Distance' モルフォロジー手法を使ったことに注意してください。通常の Distance 手法 は、画像全体に適用される特殊な2パスの高速距離手法です。このため、書き込みマスクはピクセルの「行」に沿った動作を制限せず、書き込みマスクの効果はほとんどありません。その結果、'Distance' は書き込みマスクにかかわらず、水平の隙間を「飛び越える」傾向があります。すなわち、通常の Distance 手法 は適切に「制約」されません。一方、'Iterative_Distance' 手法は、より単純な 基本モルフォロジー手法 のように機能し、局所的な近傍にのみ増分的に適用されます。これは実際には勾配形の 'dilate' に近く、実は 真のグレースケールモルフォロジー 手法と密接に関連しています。より小さな「増分ステップ」で画像を処理するので、'Iterative_Distance' 手法は書き込みマスクによって「制約」されます。残念ながら、これもまたずっと遅いです。画像を2パスする代わりに、上記はモルフォロジー手法への 反復回数 で与えた通り、150 パスを実行します。この反復回数はできるだけ小さく、ただし画像内で見つかる最大距離を覆うのに十分なほど大きく保つのが最善です。 | _書き込みマスクとともに '-1' や(ほぼ)無限の反復回数を使わないでください。IMv7 のモルフォロジーは、一部のピクセルが書き込み不可であることを理解しておらず、形状の縁の周りの「書き込み不可ピクセル」に常に変化を見るので、画像にこれ以上変化が見られなくても中断しません。
これは IMv7 で修正されることが望まれます。IMv7 は、一部のピクセルが書き込み不可であることを理解することで演算子を少し賢くする大きな内部再構築を提供し、それらを「変化した」ものとして計算したりカウントしたりするのを避けます。
_
---|---
最後に、そうしたカーネルはあまり正確でないにもかかわらず、私が半径 1 しかない 距離カーネル を使ったことに気づくでしょう。これは重要です。より大きなカーネルは、その半径より小さい任意の隙間を距離勾配が飛び越える結果になりうるからです。上記の 条件付き Dilate を参照してください。より高い精度が必要な場合は、使いたい 距離カーネル より小さい隙間がないことを保証する必要があります。それには、画像の縁の鋭い曲がりによる隙間も含まれます。
形状のスケルトン生成
作成中
HIPR2 Morphology より
http://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm
スケルトン/MAT は主に2つの方法で生成できる。1つ目は、これ以上細線化
できなくなるまで(線分の端点を保ちながら)境界からピクセルを次々に
収縮させる、ある種のモルフォロジー的細線化を使う方法で、その時点で
残ったものがスケルトンを近似する。
もう1つの方法は、まず画像の距離変換を計算する。スケルトンはその後、
距離変換における特異点(すなわち折り目や曲率の不連続)に沿って存在する。
後者のアプローチは MAT の計算により適している。MAT は、スケルトンの一部
でないすべての点をゼロに抑えた距離変換と同じだからである。
注:MAT はしばしば距離変換上の「局所最大値の軌跡」として記述される。
これは「局所最大値」という語句の通常のいかなる意味でも本当には正しくない。
距離変換を、第三の次元がグレー値を表す3次元曲面プロットとして表示すると、
MAT は3次元曲面上の稜線として想像できる。
定義??
モルフォロジースケルトン(収縮による?)、(細線化による)
スケルトンは、繰り返しの細線化によるか、距離変換によって、そして3次元曲面
上の「折り目」や稜線を見つけることで計算される(分水嶺変換?)。
mat.gif -morphology HMT Ridges -threshold 0
mat.gif -morphology HMT LineEnds -threshold 0
mat.gif -morphology HMT Ridges\;LineEnds -threshold 0
mat.gif -morphology HMT Ridges\;Ridges2 -threshold 0
mat.gif -morphology TopHat Diamond -threshold 0
mat.gif -define morphology:compose=Lighten \
-morphology TopHat '3@:-,1,- -,1,- -,-,-' -threshold 0
中心軸変換(MAT)の1つの定義は、各点の輝度を使って境界までの距離を表す。
すなわち、スケルトンが距離変換のマスクとして使われた。距離変換手法はこれ
により適しており、おそらく細線化によるより計算が速い。
SKIZ(影響領域によるスケルトン)は背景のスケルトンで、演算のネガである。
すなわち、各前景オブジェクトに最も近い領域を分割する。(太線化によって生成)
一般に SKIZ は、画像の縁に付いていない限り線分の端も収縮させることで、
単純な領域、すなわち盆地まで刈り込まれる。
スケルトンによる形状の識別。
最も遠い「端」点間の距離、
画像内の「ループ」または領域の数、
三重点の数。
距離からスケルトンへ
画像から生の「モルフォロジースケルトン」を生成する素早く手っ取り早い方法の1つは、距離勾配に '[TopHat](#tophat)' 手法を適用することです。 例えば、以下は、輪郭を少し滑らかにするために少し オープニング された後の形状のスケルトンです。 |
magick man.gif \
-morphology Open Diamond \
-morphology Distance Chebyshev \
-morphology TopHat Diamond \
-auto-level chebyshev_dist_skel.gif
![[IM Output]](../static/img/morphology/chebyshev_dist_skel.gif)
これは基本的にモルフォロジースケルトンです。これは、最大の「正方形」(より一般的には「最大球(Maximal Ball)」として知られる)が見つけられたピクセルだけを示すので、不完全に見えます。ただし、依然として多くの孤立したピクセルを伴う非常に生の結果です。驚くべきことに、これは機能します。'[Open](#open)' なしでは、形状の輪郭が非常に粗いため、結果は非常に悪くなります。 ユークリッド距離測度を使うと、形状のより良いスケルトンが生成されます。 |
magick man.gif \
-morphology Open Diamond \
-morphology Distance Euclidean:4 \
-morphology TopHat Diamond \
-auto-level euclidean_dist_skel.gif
![[IM Output]](../static/img/morphology/euclidean_dist_skel.gif)
ただし、ご覧の通り、よりノイズが多くなり、スケルトンはより完全になる一方で、多くのグレースケール値を伴って非常に汚くもなります。 以下は、スケルトンの「頭」の拡大で、それがどれほどばらばらのままかを示しています。 |
magick euclidean_dist_skel.gif -crop 35x28+30+13 +repage \
-scale 400% euclidean_dist_skel_mag.gif
![[IM Output]](../static/img/morphology/euclidean_dist_skel_mag.gif)
もちろん上記は、しきい値処理して、それを生成するのに使った実際の距離勾配とともにマスクとして使えます。これにより、スケルトンを構成する最大ディスクの実際のサイズ(距離半径)を調べられ、元の形状を再生成できます。
Autotrace によるスケルトン
スケルトン生成のもう一つの代替手段は、"[AutoTrace](http://autotrace.sourceforge.net/)" プログラムとその特殊な centerline オプションを使うことです。印刷やフォント変換への関与により、処理に対して白地に黒を前提とすることに注意してください。例えば…
magick man.gif -negate man_at_prep.pgm
autotrace -centerline -output-format svg man_at_prep.pgm |\
magick SVG:- man_centerline.gif
magick man.gif man_centerline.gif \
-compose multiply -composite man_at_skeleton.gif
生成された centerline は、プロセスのベクトル的性質により、滑らかな曲線の形であることに注意してください。これもまた切り離されており、スケルトンのループに隙間があり、枝も切り離されています。ただし、"autotrace" が実際にこのスケルトンをどう生成するかは私は調べていません。"[AutoTrace](http://autotrace.sourceforge.net/)" を使う他の例については、SVG 出力の扱い と ラスターからベクターへのエッジング を参照してください。
![[IM Output]](../static/img/morphology/kernel_corner.gif)
![[IM Output]](../static/img/morphology/kernel_gaussian.gif)
![[IM Output]](../static/img/morphology/k_diamond.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_1.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_2.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_3.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_4.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_5.gif)
![[IM Output]](../static/img/morphology/kernel_disk_01.gif)
![[IM Output]](../static/img/morphology/kernel_disk_02.gif)
![[IM Output]](../static/img/morphology/kernel_disk_03.gif)
![[IM Output]](../static/img/morphology/kernel_disk_04.gif)
![[IM Output]](../static/img/morphology/kernel_disk_05.gif)
![[IM Output]](../static/img/morphology/kernel_disk_06.gif)
![[IM Output]](../static/img/morphology/kernel_disk_07.gif)
![[IM Output]](../static/img/morphology/kernel_disk_08.gif)
![[IM Output]](../static/img/morphology/kernel_disk_09.gif)
![[IM Output]](../static/img/morphology/kernel_disk_10.gif)
![[IM Output]](../static/img/morphology/kernel_disk_11.gif)
![[IM Output]](../static/img/morphology/kernel_plus_1.gif)
![[IM Output]](../static/img/morphology/kernel_plus_2.gif)
![[IM Output]](../static/img/morphology/kernel_plus_3.gif)
![[IM Output]](../static/img/morphology/kernel_plus_4.gif)
![[IM Output]](../static/img/morphology/kernel_cross_1.gif)
![[IM Output]](../static/img/morphology/kernel_cross_2.gif)
![[IM Output]](../static/img/morphology/kernel_cross_3.gif)
![[IM Output]](../static/img/morphology/kernel_cross_4.gif)
![[IM Output]](../static/img/morphology/kernel_ring.gif)
![[IM Output]](../static/img/morphology/kernel_ring_01.gif)
![[IM Output]](../static/img/morphology/kernel_ring_02.gif)
![[IM Output]](../static/img/morphology/kernel_ring_03.gif)
![[IM Output]](../static/img/morphology/kernel_ring_04.gif)
![[IM Output]](../static/img/morphology/kernel_ring_05.gif)
![[IM Output]](../static/img/morphology/kernel_ring_06.gif)
![[IM Output]](../static/img/morphology/kernel_ring_07.gif)
![[IM Output]](../static/img/morphology/kernel_ring_08.gif)
![[IM Output]](../static/img/morphology/kernel_ring_09.gif)
![[IM Output]](../static/img/morphology/kernel_ring_10.gif)
![[IM Output]](../static/img/morphology/kernel_ring_11.gif)
![[IM Output]](../static/img/morphology/kernel_ring_12.gif)
![[IM Output]](../static/img/morphology/kernel_ring_13.gif)
![[IM Output]](../static/img/morphology/kernel_ring_14.gif)
![[IM Output]](../static/img/morphology/kernel_ring_15.gif)
![[IM Output]](../static/img/morphology/kernel_ring_16.gif)
![[IM Output]](../static/img/morphology/kernel_ring_17.gif)
![[IM Output]](../static/img/morphology/kernel_ring_18.gif)
![[IM Output]](../static/img/morphology/kernel_ring_19.gif)
![[IM Output]](../static/img/morphology/kernel_ring_20.gif)
![[IM Output]](../static/img/morphology/kernel_ring_21.gif)
![[IM Output]](../static/img/morphology/kernel_rect_1.gif)
![[IM Output]](../static/img/morphology/kernel_rect_2.gif)
![[IM Output]](../static/img/morphology/kernel_rect_3.gif)
![[IM Output]](../static/img/morphology/kernel_rect_4.gif)
![[IM Output]](../static/img/morphology/kernel_rect_5.gif)
![[IM Output]](../static/img/morphology/kernel_rect_6.gif)
![[IM Output]](../static/img/morphology/kernel_rect_7.gif)
![[IM Output]](../static/img/morphology/kernel_rect_8.gif)
![[IM Output]](../static/img/morphology/flagged_points.gif)
![[IM Output]](../static/img/morphology/iterate_infinite.gif)
![[IM Text]](../static/img/morphology/verbose_iterate.txt.gif)
![[IM Text]](../static/img/morphology/verbose_compound.txt.gif)
![[IM Text]](../static/img/morphology/k_disk.txt.gif)
![[IM Text]](../static/img/morphology/k_comet.txt.gif)
![[IM Text]](../static/img/morphology/k_precision.txt.gif)
![[IM Text]](../static/img/morphology/k_multi.txt.gif)
![[IM Text]](../static/img/morphology/kernel_multi.gif)
![[IM Text]](../static/img/morphology/kernel_rotated_list.gif)
![[IM Text]](../static/img/morphology/kernel_rotated_list2.gif)
![[IM Text]](../static/img/morphology/blur_kernels.gif)
![[IM Output]](../static/img/morphology/blur_kernel.gif)
![[IM Output]](../static/img/morphology/blur_kernel2.gif)
![[IM Output]](../static/img/morphology/blur_re-iterate.gif)
![[IM Output]](../static/img/morphology/blur_1_mag.gif)
![[IM Output]](../static/img/morphology/blur_union_mag.gif)
![[IM Output]](../static/img/morphology/blur_2_mag.gif)
![[IM Text]](../static/img/morphology/blur_re-iterate.txt.gif)
![[IM Text]](../static/img/morphology/blur_union.txt.gif)
![[IM Output]](../static/img/morphology/erode_man.gif)
![[IM Output]](../static/img/morphology/dilate_man.gif)
![[IM Output]](../static/img/morphology/dilate_man_neg.gif)
![[IM Output]](../static/img/morphology/open_man.gif)
![[IM Output]](../static/img/morphology/open_erode.gif)
![[IM Output]](../static/img/morphology/open_man_2.gif)
![[IM Output]](../static/img/morphology/open_man_twice.gif)
![[IM Output]](../static/img/morphology/close_man.gif)
![[IM Output]](../static/img/morphology/close_dilate.gif)
![[IM Output]](../static/img/morphology/close_man_2.gif)
![[IM Output]](../static/img/morphology/close_man_neg.gif)
![[IM Output]](../static/img/images/rose.gif)
![[IM Output]](../static/img/morphology/rose_dilate.gif)
![[IM Output]](../static/img/morphology/rose_dilate_intensity.gif)
![[IM Output]](../static/img/morphology/rose_erode_intensity.gif)
![[IM Output]](../static/img/morphology/rose_open_intensity.gif)
![[IM Output]](../static/img/morphology/edgein_man.gif)
![[IM Output]](../static/img/morphology/edgeout_man.gif)
![[IM Output]](../static/img/morphology/edge_man.gif)
![[IM Output]](../static/img/morphology/tophat_man.gif)
![[IM Output]](../static/img/morphology/bottomhat_man.gif)
![[IM Output]](../static/img/images/figure.gif)
![[IM Output]](../static/img/morphology/kernel_diamond_3.gif)
![[IM Output]](../static/img/morphology/figure_erode.gif)
![[IM Text]](../static/img/morphology/test_mag.gif)
![[IM Output]](../static/img/morphology/kernel_right.gif)
![[IM Output]](../static/img/morphology/hmt_right.gif)
![[IM Output]](../static/img/morphology/kernel_right2.gif)
![[IM Output]](../static/img/morphology/hmt_right2.gif)
![[IM Output]](../static/img/morphology/kernel_nw_corner.gif)
![[IM Output]](../static/img/morphology/hmt_nw_corner.gif)
![[IM Output]](../static/img/morphology/lines.gif)
![[IM Output]](../static/img/morphology/hmt_junctions.gif)
![[IM Output]](../static/img/morphology/kernel_right_out.gif)
![[IM Output]](../static/img/morphology/thick_right.gif)
![[IM Output]](../static/img/morphology/hmt_thicken.gif)
![[IM Output]](../static/img/morphology/man_line.gif)
![[IM Output]](../static/img/morphology/thick_line.gif)
![[IM Output]](../static/img/morphology/man_convex_edge.gif)
![[IM Output]](../static/img/morphology/man_extremities.gif)
![[IM Output]](../static/img/morphology/man_grey.gif)
![[IM Output]](../static/img/morphology/thick_corners.gif)
![[IM Output]](../static/img/morphology/kernel_right_in.gif)
![[IM Output]](../static/img/morphology/thin_right.gif)
![[IM Output]](../static/img/morphology/hmt_thinning.gif)
![[IM Output]](../static/img/morphology/man_raw_thinned.gif)
![[IM Output]](../static/img/morphology/man_erode.gif)
![[IM Output]](../static/img/morphology/man_skeleton.gif)
![[IM Output]](../static/img/morphology/man_thin_skeleton.gif)
![[IM Output]](../static/img/morphology/man_ends_marked.gif)
![[IM Output]](../static/img/morphology/man_junctions.gif)
![[IM Output]](../static/img/morphology/man_junctions_marked.gif)
![[IM Output]](../static/img/morphology/man_loop.gif)
![[IM Output]](../static/img/morphology/line_mag.gif)
![[IM Output]](../static/img/morphology/line_seqential_mag.gif)
![[IM Output]](../static/img/morphology/rect_mag.gif)
![[IM Output]](../static/img/morphology/rect_simultaneous_mag.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_01.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_02.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_03.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_04.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_05.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_06.gif)
![[IM Output]](../static/img/morphology/kernel_edges.gif)
![[IM Output]](../static/img/morphology/thin_edges.gif)
![[IM Output]](../static/img/morphology/kernel_corners.gif)
![[IM Output]](../static/img/morphology/kernel_diagonals.gif)
![[IM Output]](../static/img/morphology/kernel_diagonals1.gif)
![[IM Output]](../static/img/morphology/kernel_diagonals2.gif)
![[IM Output]](../static/img/morphology/kernel_lineends.gif)
![[IM Output]](../static/img/morphology/hmt_lineends.gif)
![[IM Output]](../static/img/morphology/kernel_lineends1.gif)
![[IM Output]](../static/img/morphology/kernel_lineends2.gif)
![[IM Output]](../static/img/morphology/kernel_lineends3.gif)
![[IM Output]](../static/img/morphology/kernel_lineends4.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions1.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions2.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions3.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions4.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions5.gif)
![[IM Output]](../static/img/morphology/kernel_ridges.gif)
![[IM Output]](../static/img/morphology/kernel_ridges2.gif)
![[IM Output]](../static/img/morphology/kernel_convexhull.gif)
![[IM Output]](../static/img/morphology/man_hull.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_4strong.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_4weak.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_8strong.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_8weak.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_23comb.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_48gen.gif)
![[IM Output]](../static/img/morphology/distance.png)
![[IM Text]](../static/img/morphology/distance_max.txt.gif)
![[IM Output]](../static/img/morphology/man_clean.gif)
![[IM Output]](../static/img/morphology/distance_clean.gif)
![[IM Output]](../static/img/morphology/man_floodfill.gif)
![[IM Output]](../static/img/morphology/distance_floodfill.gif)
![[IM Text]](../static/img/morphology/k_chebyshev_3.txt.gif)
![[IM Text]](../static/img/morphology/k_chebyshev.txt.gif)
![[IM Output]](../static/img/morphology/chebyshev_gradient.gif)
![[IM Text]](../static/img/morphology/chebyshev_gradient.txt.gif)
![[IM Text]](../static/img/morphology/chebyshev_distance.txt.gif)
![[IM Text]](../static/img/morphology/k_manhattan.txt.gif)
![[IM Output]](../static/img/morphology/manhattan_gradient.gif)
![[IM Text]](../static/img/morphology/manhattan_distance.txt.gif)
![[IM Text]](../static/img/morphology/k_octagonal.txt.gif)
![[IM Output]](../static/img/morphology/octagonal_gradient.gif)
![[IM Text]](../static/img/morphology/octagonal_distance.txt.gif)
![[IM Output]](../static/img/morphology/fractional_gradient.gif)
![[IM Text]](../static/img/morphology/fractional_distance.txt.gif)
![[IM Text]](../static/img/morphology/k_knight.txt.gif)
![[IM Output]](../static/img/morphology/knight_gradient.gif)
![[IM Text]](../static/img/morphology/knight_distance.txt.gif)
![[IM Text]](../static/img/morphology/k_euclidean.txt.gif)
![[IM Output]](../static/img/morphology/euclidean_gradient.gif)
![[IM Text]](../static/img/morphology/euclidean_distance.txt.gif)
![[IM Output]](../static/img/morphology/circle_shape.png)
![[IM Output]](../static/img/morphology/cone_distance.png)
![[IM Output]](../static/img/morphology/cone_distance_shade.png)
![[IM Output]](../static/img/morphology/circle_gradient.png)
![[IM Output]](../static/img/morphology/circle_ridge.png)
![[IM Output]](../static/img/morphology/rose_shape.png)
![[IM Output]](../static/img/morphology/rose_feathered.png)
![[IM Output]](../static/img/morphology/boolean_feathered.png)
![[IM Output]](../static/img/images/disks.gif)
![[IM Output]](../static/img/morphology/cond_dilate_draw.gif)
![[IM Output]](../static/img/morphology/disks_big_center.gif)
![[IM Output]](../static/img/morphology/disks_mask.gif)
![[IM Output]](../static/img/morphology/disks_big_found.gif)
![[IM Output]](../static/img/morphology/disks_line.gif)
![[IM Output]](../static/img/morphology/disks_line_find.gif)
![[IM Output]](../static/img/morphology/disks_line_found.gif)
![[IM Output]](../static/img/morphology/distance_start.png)
![[IM Output]](../static/img/morphology/distance_point.png)
![[IM Output]](../static/img/morphology/distance_bounds.png)
![[IM Output]](../static/img/morphology/distance_direct.png)
![[IM Output]](../static/img/morphology/distance_constrained.png)
![[IM Output]](../static/img/morphology/man_at_prep.png)
![[IM Output]](../static/img/morphology/man_centerline.gif)
![[IM Output]](../static/img/morphology/man_at_skeleton.gif)