Open in Colab

Image stitching example with LoFTR

First, we will install everything needed:

%%capture
!pip install kornia scikit-image

Now let’s download an image pair

%%capture
import matplotlib.pyplot as plt
import numpy as np
from skimage import io

import torch
import kornia as K
import kornia.feature as KF


def load_torch_image(fname):
    return K.image_to_tensor(io.imread(fname), False).float() / 255.

def load_images_by_link(links):
    return list([load_torch_image(fname).cuda() for fname in links])

fnames = [
    'http://www.ic.unicamp.br/~helio/imagens_registro/foto1B.jpg',
    'http://www.ic.unicamp.br/~helio/imagens_registro/foto1A.jpg',
]

imgs = load_images_by_link(fnames)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
/tmp/ipykernel_1039/1852754037.py in <module>
     19 ]
     20 
---> 21 imgs = load_images_by_link(fnames)

/tmp/ipykernel_1039/1852754037.py in load_images_by_link(links)
     12 
     13 def load_images_by_link(links):
---> 14     return list([load_torch_image(fname).cuda() for fname in links])
     15 
     16 fnames = [

/tmp/ipykernel_1039/1852754037.py in <listcomp>(.0)
     12 
     13 def load_images_by_link(links):
---> 14     return list([load_torch_image(fname).cuda() for fname in links])
     15 
     16 fnames = [

~/checkouts/readthedocs.org/user_builds/kornia-tutorials/envs/latest/lib/python3.7/site-packages/torch/cuda/__init__.py in _lazy_init()
    212         # This function throws if there's a driver initialization error, no GPUs
    213         # are found or any other error occurs
--> 214         torch._C._cuda_init()
    215         # Some of the queued calls may reentrantly call _lazy_init();
    216         # we need to just return without initializing in that case.

RuntimeError: Found no NVIDIA driver on your system. Please check that you have an NVIDIA GPU and installed a driver from http://www.nvidia.com/Download/index.aspx

Stitch them together

from kornia.contrib import ImageStitcher

IS = ImageStitcher(KF.LoFTR(pretrained='outdoor'), estimator='ransac').cuda()

with torch.no_grad():
    out = IS(*imgs)

plt.imshow(K.tensor_to_image(out))
plt.show()

Another example

fnames = [
    'https://github.com/daeyun/Image-Stitching/blob/master/img/hill/1.JPG?raw=true',
    'https://github.com/daeyun/Image-Stitching/blob/master/img/hill/2.JPG?raw=true',
    'https://github.com/daeyun/Image-Stitching/blob/master/img/hill/3.JPG?raw=true',
]

imgs = load_images_by_link(fnames)
f, axarr = plt.subplots(1, 3, figsize = (16,6))

axarr[0].imshow(K.tensor_to_image(imgs[0]))
axarr[0].tick_params(left = False, right = False, labelleft = False, labelbottom = False, bottom = False)
axarr[1].imshow(K.tensor_to_image(imgs[1]))
axarr[1].tick_params(left = False, right = False, labelleft = False, labelbottom = False, bottom = False)
axarr[2].imshow(K.tensor_to_image(imgs[2]))
axarr[2].tick_params(left = False, right = False, labelleft = False, labelbottom = False, bottom = False)
_images/image_stitching_8_0.png
matcher = KF.LocalFeatureMatcher(
    KF.GFTTAffNetHardNet(100), KF.DescriptorMatcher('snn', 0.8)
)
IS = ImageStitcher(matcher, estimator='ransac').cuda()

with torch.no_grad():
    out = IS(*imgs)

plt.figure(figsize=(16, 16))
plt.imshow(K.tensor_to_image(out))
/usr/local/lib/python3.7/dist-packages/kornia/feature/loftr/utils/coarse_matching.py:255: UserWarning: __floordiv__ is deprecated, and its behavior will change in a future version of pytorch. It currently rounds toward 0 (like the 'trunc' function NOT 'floor'). This results in incorrect rounding for negative values. To keep the current behavior, use torch.div(a, b, rounding_mode='trunc'), or for actual floor division, use torch.div(a, b, rounding_mode='floor').
  [i_ids % data['hw0_c'][1], i_ids // data['hw0_c'][1]],
/usr/local/lib/python3.7/dist-packages/kornia/feature/loftr/utils/coarse_matching.py:258: UserWarning: __floordiv__ is deprecated, and its behavior will change in a future version of pytorch. It currently rounds toward 0 (like the 'trunc' function NOT 'floor'). This results in incorrect rounding for negative values. To keep the current behavior, use torch.div(a, b, rounding_mode='trunc'), or for actual floor division, use torch.div(a, b, rounding_mode='floor').
  [j_ids % data['hw1_c'][1], j_ids // data['hw1_c'][1]],
<matplotlib.image.AxesImage at 0x7fea2a275510>
_images/image_stitching_9_2.png