Як я вже говорив у коментарях, реєстрація медичних зображень - це тема з великою кількістю досліджень, і я не є експертом. З того, що я читав, основна ідея, яка зазвичай використовується, - це визначити відображення між двома зображеннями (у вашому випадку зображення та його дзеркальним зображенням), потім визначити енергетичні терміни для гладкості та подібності зображення, якщо застосовується відображення, і нарешті оптимізувати це відображення за допомогою стандартних (або іноді конкретних прикладних) методів оптимізації.
Я зламав швидкий алгоритм в 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]
Зазвичай ми робимо приблизну жорстку реєстрацію (використовуючи, наприклад, ключові точки або моменти зображення), але ваше зображення майже в центрі, тому я пропускаю це.
Якщо ми подивимось на один блок і це аналог дзеркального зображення:
{partsS[[6, 10]], partsM[[6, 10]]}
Ми можемо бачити, що вони схожі, але зміщені. Ми намагаємося з’ясувати кількість та напрямок зміни.
Для кількісної оцінки подібності відповідності я можу використовувати евклідову відстань у квадраті:
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
наближає різну евклідову різницю між початковим зображенням та дзеркальним зображенням із застосованими зрушеннями.
Оптимізація однієї цієї енергії дала б погані результати (якщо вона взагалі сходилася). Ми також хочемо, щоб компенсації були плавними, коли схожість блоку не означає нічого про зміщення (наприклад, по прямій лінії або на білому тлі).
Отже, ми створили другий енергетичний термін для плавності:
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
- відносна вага енергії гладкості, яку отримує залежно від терміна енергії відповідності зображення. Це результати для різної ваги:
Можливі вдосконалення:
- Як я вже сказав, спочатку проведіть жорстку реєстрацію. З білим тлом реєстрація на основі простих зображень повинна працювати чудово.
- Це лише один крок. Ви можете використовувати компенсації, знайдені за один крок, та вдосконалити їх на другому кроці, можливо, за допомогою меншого вікна пошуку чи менших розмірів блоків
- Я читав статті, де вони взагалі роблять це без блоків, але оптимізують зміщення на піксель.
- Спробуйте різні функції гладкості