コメントで述べたように、医療画像の登録は多くの研究が利用可能なトピックであり、私は専門家ではありません。私が読んだことから、一般的に使用される基本的な考え方は、2つの画像(あなたの場合は画像とその鏡像)間のマッピングを定義し、マッピングが適用された場合の滑らかさと画像の類似性のエネルギー項を定義し、最後に標準の(またはアプリケーション固有の)最適化手法を使用してこのマッピングを最適化します。
これを実証するためにMathematicaで簡単なアルゴリズムをハッキングしました。これは、医療アプリケーションで使用すべきアルゴリズムではなく、基本的なアイデアのデモにすぎません。
最初に、イメージをロードし、ミラーリングして、これらのイメージを小さなブロックに分割します。
src = ColorConvert[Import["http://i.stack.imgur.com/jf709.jpg"],
"Grayscale"];
mirror = ImageReflect[src, Left -> Right];
blockSize = 30;
partsS = ImagePartition[src, {blockSize, blockSize}];
partsM = ImagePartition[mirror, {blockSize, blockSize}];
GraphicsGrid[partsS]
通常、キーポイントや画像モーメントなどを使用して、おおよその厳密な登録を行いますが、画像はほぼ中央にあるため、これはスキップします。
1つのブロックを見て、それが鏡像に相当する場合:
{partsS[[6, 10]], partsM[[6, 10]]}
それらは似ているが、シフトしていることがわかります。シフトの量と方向は、私たちが見つけようとしているものです。
一致の類似性を定量化するために、ユークリッド距離の2乗を使用できます。
ListPlot3D[
ImageData[
ImageCorrelate[partsM[[6, 10]], partsS[[6, 10]],
SquaredEuclideanDistance]]]
悲しいことに、このデータを使用すると、最適化が直接考えられていたよりも困難であったため、代わりに2次近似を使用しました。
fitTerms = {1, x, x^2, y, y^2, x*y};
fit = Fit[
Flatten[MapIndexed[{#2[[1]] - blockSize/2, #2[[2]] -
blockSize/2, #1} &,
ImageData[
ImageCorrelate[partsM[[6, 10]], partsS[[6, 10]],
SquaredEuclideanDistance]], {2}], 1], fitTerms, {x, y}];
Plot3D[fit, {x, -25, 25}, {y, -25, 25}]
この関数は実際の相関関数と同じではありませんが、最初のステップに十分近いものです。ブロックのすべてのペアに対してこれを計算しましょう:
distancesFit = MapThread[
Function[{part, template},
Fit[Flatten[
MapIndexed[{#2[[2]] - blockSize/2, #2[[1]] - blockSize/2, #1} &,
ImageData[
ImageCorrelate[part, template,
SquaredEuclideanDistance]], {2}], 1],
fitTerms, {x, y}]], {partsM, partsS}, 2];
これにより、最適化の最初のエネルギー項が得られます。
variablesX = Array[dx, Dimensions[partsS]];
variablesY = Array[dy, Dimensions[partsS]];
matchEnergyFit =
Total[MapThread[#1 /. {x -> #2, y -> #3} &, {distancesFit,
variablesX, variablesY}, 2], 3];
variablesX/Y
各ブロックのオフセットが含まれています。 matchEnergyFit
元の画像とオフセットが適用されたミラー化された画像との間の平方ユークリッド差近似します。
このエネルギーだけを最適化すると、悪い結果が得られます(収束した場合)。また、オフセットを滑らかにする必要があります。この場合、ブロックの類似性はオフセットについて何も伝えません(たとえば、直線に沿って、または白い背景で)。
そこで、平滑化のために2番目のエネルギー項を設定します。
smoothnessEnergy = Total[Flatten[
{
Table[
variablesX[[i, j - 1]] - 2 variablesX[[i, j]] +
variablesX[[i, j + 1]], {i, 1, Length[partsS]}, {j, 2,
Length[partsS[[1]]] - 1}],
Table[
variablesX[[i - 1, j]] - 2 variablesX[[i, j]] +
variablesX[[i + 1, j]], {i, 2, Length[partsS] - 1}, {j, 1,
Length[partsS[[1]]]}],
Table[
variablesY[[i, j - 1]] - 2 variablesY[[i, j]] +
variablesY[[i, j + 1]], {i, 1, Length[partsS]}, {j, 2,
Length[partsS[[1]]] - 1}],
Table[
variablesY[[i - 1, j]] - 2 variablesY[[i, j]] +
variablesY[[i + 1, j]], {i, 2, Length[partsS] - 1}, {j, 1,
Length[partsS[[1]]]}]
}^2]];
幸いなことに、制約付き最適化はMathematicaに組み込まれています。
allVariables = Flatten[{variablesX, variablesY}];
constraints = -blockSize/3. < # < blockSize/3. & /@ allVariables;
initialValues = {#, 0} & /@ allVariables;
solution =
FindMinimum[{matchEnergyFit + 0.1 smoothnessEnergy, constraints},
initialValues];
結果を見てみましょう:
grid = Table[{(j - 0.5)*blockSize - dx[i, j], (i - 0.5)*blockSize -
dy[i, j]}, {i, Length[partsS]}, {j, Length[partsS[[1]]]}] /.
solution[[2]];
Show[src, Graphics[
{Red,
Line /@ grid,
Line /@ Transpose[grid]
}]]
0.1
前の要因smoothnessEnergy
は、平滑エネルギーが画像一致エネルギー項に関連して得られる相対的な重みです。これらは異なる重みの結果です:
可能な改善:
- 私が言ったように、最初に厳格な登録を実行します。背景が白の場合、単純な画像の瞬間ベースの登録は正常に機能するはずです。
- これは1つのステップにすぎません。1つのステップで見つけたオフセットを使用し、2番目のステップでそれらを改善できます。たとえば、検索ウィンドウを小さくするか、ブロックサイズを小さくします。
- ブロックなしでこれを行う記事を読みましたが、ピクセルごとのオフセットを最適化します。
- さまざまな平滑化機能を試す