Như tôi đã nói trong các bình luận, đăng ký hình ảnh y tế là một chủ đề có rất nhiều nghiên cứu có sẵn, và tôi không phải là một chuyên gia. Từ những gì tôi đã đọc, ý tưởng cơ bản thường được sử dụng là xác định ánh xạ giữa hai hình ảnh (trong trường hợp của bạn là hình ảnh và hình ảnh phản chiếu của nó), sau đó xác định thuật ngữ năng lượng cho độ mịn và độ tương tự hình ảnh nếu áp dụng ánh xạ và cuối cùng tối ưu hóa ánh xạ này bằng cách sử dụng các kỹ thuật tối ưu hóa tiêu chuẩn (hoặc đôi khi dành riêng cho ứng dụng).
Tôi đã hack cùng một thuật toán nhanh trong Mathematica để chứng minh điều này. Đây không phải là một thuật toán bạn nên sử dụng trong một ứng dụng y tế, chỉ là một minh chứng cho những ý tưởng cơ bản.
Đầu tiên, tôi tải hình ảnh của bạn, phản chiếu nó và chia những hình ảnh này thành các khối nhỏ:
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]
Thông thường, chúng tôi sẽ thực hiện đăng ký cứng nhắc gần đúng (sử dụng ví dụ: điểm chính hoặc khoảnh khắc hình ảnh), nhưng hình ảnh của bạn gần như được căn giữa, vì vậy tôi sẽ bỏ qua điều này.
Nếu chúng ta nhìn vào một khối và đó là bản sao hình ảnh phản chiếu:
{partsS[[6, 10]], partsM[[6, 10]]}
Chúng ta có thể thấy rằng chúng tương tự nhau, nhưng đã thay đổi. Số lượng và hướng của sự thay đổi là những gì chúng tôi đang cố gắng tìm hiểu.
Để định lượng độ tương tự của trận đấu, tôi có thể sử dụng khoảng cách euclide bình phương:
ListPlot3D[
ImageData[
ImageCorrelate[partsM[[6, 10]], partsS[[6, 10]],
SquaredEuclideanDistance]]]
thật đáng buồn, sử dụng dữ liệu này là tối ưu hóa trực tiếp khó hơn tôi nghĩ, vì vậy tôi đã sử dụng xấp xỉ bậc 2 thay thế:
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}]
Hàm này không giống như hàm tương quan thực tế, nhưng nó đủ gần cho bước đầu tiên. Hãy tính toán điều này cho mỗi cặp khối:
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];
Điều này cho chúng tôi thuật ngữ năng lượng đầu tiên của chúng tôi để tối ưu hóa:
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
chứa các độ lệch cho mỗi khối và matchEnergyFit
xấp xỉ mức chênh lệch euclide bình phương giữa ảnh gốc và ảnh được nhân đôi với các độ lệch được áp dụng.
Tối ưu hóa năng lượng này một mình sẽ cho kết quả kém (nếu nó hội tụ hoàn toàn). Chúng tôi cũng muốn các độ lệch được trơn tru, trong đó độ tương tự khối không nói gì về phần bù (ví dụ: dọc theo một đường thẳng hoặc trong nền trắng).
Vì vậy, chúng tôi thiết lập một thuật ngữ năng lượng thứ hai cho sự trơn tru:
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]];
May mắn thay, tối ưu hóa bị ràng buộc được tích hợp sẵn trong Mathematica:
allVariables = Flatten[{variablesX, variablesY}];
constraints = -blockSize/3. < # < blockSize/3. & /@ allVariables;
initialValues = {#, 0} & /@ allVariables;
solution =
FindMinimum[{matchEnergyFit + 0.1 smoothnessEnergy, constraints},
initialValues];
Hãy nhìn vào kết quả:
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]
}]]
Các 0.1
yếu tố trước khi smoothnessEnergy
là trọng lượng tương đối năng lượng êm ái được liên quan đến thuật ngữ hình ảnh trận đấu năng lượng. Đây là kết quả cho các trọng lượng khác nhau:
Những cải tiến có thể có:
- Như tôi đã nói, thực hiện đăng ký cứng nhắc đầu tiên. Với nền trắng, đăng ký dựa trên khoảnh khắc hình ảnh đơn giản sẽ hoạt động tốt.
- Đây chỉ là một bước. Bạn có thể sử dụng các offset bạn tìm thấy trong một bước và cải thiện chúng trong bước thứ hai, có thể với cửa sổ tìm kiếm nhỏ hơn hoặc kích thước khối nhỏ hơn
- Tôi đã đọc các bài báo nơi họ làm điều này mà không có khối nào cả, nhưng tối ưu hóa bù cho mỗi pixel.
- Hãy thử các chức năng làm mịn khác nhau