Новые методы «особых точек» (features) на изображении и LightGlue
"Особые точки", особенности или features используются в компьютерном зрении достаточно давно, в том числе и в OpenCV, в котором они первоначально были представлены алгоритмом SURF. Алгоритм был тяжеловесный, использующий матрицу Гессе и по началу был в свободном доступе вместе с OpenCV, а потом оказалось, что его лицензия ограничена и начали встраиваться другие алгоритмы, в том числе SIFT, который популярен до сих пор.
Назначение особых точек: сопоставление точек на изображениях для определения поворота объекта и 3D реконструкции.

Наиболее популярные особенности на настоящий момент:
- SIFT (2004 год) https://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf;
- SuperPoint (2017 год) https://arxiv.org/abs/1712.07629;
- DISK (2020 год) https://arxiv.org/abs/2006.13566;
- ALIKED (2023 год) https://arxiv.org/abs/2304.03608.
При этом точность ALIKED при изменении масштаба и поворота значительно выше:

Данные особые точки использует проект LightGlue:
https://github.com/cvg/LightGlue
LightGlue - легкое средство сопоставления особенностей с высокой точностью и быстрым выводом. Он принимает на вход набор ключевых точек и дескрипторов для каждого изображения и возвращает индексы соответствующих точек.
Установка:
!git clone https://github.com/cvg/LightGlue.git
%cd LightGlue
!python -m pip install -e .
Инициализация:
from lightglue import LightGlue, SuperPoint, DISK,ALIKED
from lightglue.utils import load_image, rbd
from lightglue import viz2d
import torch
torch.set_grad_enabled(False)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 'mps', 'cpu'
extractor = SuperPoint(max_num_keypoints=2048).eval().to(device) # load the extractor
matcher = LightGlue(features="superpoint").eval().to(device)
Скачиваем фото Эрмитажа:
!wget https://cdn.tripster.ru/thumbs2/445d1fbe-1524-11eb-bd4f-9a1addf271b5.800x600.jpg
!wget https://xn--c1acndtdamdoc1ib.xn--p1ai/upload/iblock/115/IMG_6391.jpg
Находим особые точки и ищем совпадения:
imname1 = "445d1fbe-1524-11eb-bd4f-9a1addf271b5.800x600.jpg"
imname2 = "IMG_6391.jpg"
image0 = load_image(imname1)
image1 = load_image(imname2)
feats0 = extractor.extract(image0.to(device))
feats1 = extractor.extract(image1.to(device))
matches01 = matcher({"image0": feats0, "image1": feats1})
feats0, feats1, matches01 = [
rbd(x) for x in [feats0, feats1, matches01]
] # remove batch dimension
kpts0, kpts1, matches = feats0["keypoints"], feats1["keypoints"], matches01["matches"]
m_kpts0, m_kpts1 = kpts0[matches[..., 0]], kpts1[matches[..., 1]]
axes = viz2d.plot_images([image0, image1])
viz2d.plot_matches(m_kpts0, m_kpts1, color="lime", lw=0.2)
viz2d.add_text(0, f'Stop after {matches01["stop"]} layers', fs=20)
kpc0, kpc1 = viz2d.cm_prune(matches01["prune0"]), viz2d.cm_prune(matches01["prune1"])
viz2d.plot_images([image0, image1])
viz2d.plot_keypoints([kpts0, kpts1], colors=[kpc0, kpc1], ps=10)
Результат:
