Showing preview only (1,707K chars total). Download the full file or copy to clipboard to get everything.
Repository: qianqianwang68/caps
Branch: master
Commit: 1cb601a2b77f
Files: 20
Total size: 1.6 MB
Directory structure:
gitextract_bzvz72tp/
├── .gitignore
├── CAPS/
│ ├── __init__.py
│ ├── caps_model.py
│ ├── criterion.py
│ └── network.py
├── LICENSE
├── README.md
├── config.py
├── configs/
│ ├── extract_features_hpatches.yaml
│ └── train_megadepth.yaml
├── dataloader/
│ ├── __init__.py
│ ├── data_utils.py
│ └── megadepth.py
├── environment.yml
├── extract_features.py
├── jupyter/
│ ├── functions.py
│ └── visualization.ipynb
├── test/
│ └── eval_pose_megadepth.py
├── train.py
└── utils.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
__pycache__/
CAPS/__pycache__/
jupyter/.ipynb_checkpoints/
jupyter/__pycache__/
logs/
out/
models/
================================================
FILE: CAPS/__init__.py
================================================
================================================
FILE: CAPS/caps_model.py
================================================
import os
import cv2
import numpy as np
import torch
from torch import optim
from torch.autograd import Variable
import utils
import torchvision.utils as vutils
from CAPS.criterion import CtoFCriterion
from CAPS.network import CAPSNet
class CAPSModel():
def name(self):
return 'CAPS Model'
def __init__(self, args):
self.args = args
self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
# init model, optimizer, scheduler
self.model = CAPSNet(args, self.device)
self.optimizer = optim.Adam(self.model.parameters(), lr=self.args.lr)
self.scheduler = torch.optim.lr_scheduler.StepLR(self.optimizer,
step_size=args.lrate_decay_steps,
gamma=args.lrate_decay_factor)
# reloading from checkpoints
self.start_step = self.load_from_ckpt()
# define loss function
self.criterion = CtoFCriterion(args).to(self.device)
def set_input(self, data):
self.im1 = Variable(data['im1'].to(self.device))
self.im2 = Variable(data['im2'].to(self.device))
self.coord1 = Variable(data['coord1'].to(self.device))
self.fmatrix = data['F'].cuda()
self.pose = Variable(data['pose'].to(self.device))
self.intrinsic1 = data['intrinsic1'].to(self.device)
self.intrinsic2 = data['intrinsic2'].to(self.device)
self.im1_ori = data['im1_ori']
self.im2_ori = data['im2_ori']
self.batch_size = len(self.im1)
self.imsize = self.im1.size()[2:]
def forward(self):
self.out = self.model.forward(self.im1, self.im2, self.coord1)
def backward_net(self):
loss = self.criterion(self.coord1, self.out, self.fmatrix, self.pose, self.imsize)
self.j_loss, self.eloss_c, self.eloss_f, self.closs_c, self.closs_f, self.std_loss = loss
self.j_loss.backward()
def optimize_parameters(self):
self.optimizer.zero_grad()
self.forward()
self.backward_net()
self.optimizer.step()
self.scheduler.step()
def test(self):
self.model.eval()
with torch.no_grad():
coord2_e, std = self.model.test(self.im1, self.im2, self.coord1)
return coord2_e, std
def extract_features(self, im, coord):
self.model.eval()
with torch.no_grad():
feat_c, feat_f = self.model.extract_features(im, coord)
return feat_c, feat_f
def write_summary(self, writer, n_iter):
print("%s | Step: %d, Loss: %2.5f" % (self.args.exp_name, n_iter, self.j_loss.item()))
# write scalar
if n_iter % self.args.log_scalar_interval == 0:
writer.add_scalar('Total_loss', self.j_loss.item(), n_iter)
writer.add_scalar('epipolar_loss_coarse', self.eloss_c.item(), n_iter)
writer.add_scalar('epipolar_loss_fine', self.eloss_f.item(), n_iter)
writer.add_scalar('cycle_loss_coarse', self.closs_c.item(), n_iter)
writer.add_scalar('cycle_loss_fine', self.closs_f.item(), n_iter)
# write image
if n_iter % self.args.log_img_interval == 0:
# this visualization shows a number of query points in the first image,
# and their predicted correspondences in the second image,
# the groundtruth epipolar lines for the query points are plotted in the second image
num_kpts_display = 20
im1_o = self.im1_ori[0].numpy()
im2_o = self.im2_ori[0].numpy()
kpt1 = self.coord1.cpu().numpy()[0][:num_kpts_display, :]
# predicted correspondence
correspondence = self.out['coord2_ef']
kpt2 = correspondence.detach().cpu().numpy()[0][:num_kpts_display, :]
lines2 = cv2.computeCorrespondEpilines(kpt1.reshape(-1, 1, 2), 1, self.fmatrix[0].cpu().numpy())
lines2 = lines2.reshape(-1, 3)
im2_o, im1_o = utils.drawlines(im2_o, im1_o, lines2, kpt2, kpt1)
vis = np.concatenate((im1_o, im2_o), 1)
vis = torch.from_numpy(vis.transpose(2, 0, 1)).float().unsqueeze(0)
x = vutils.make_grid(vis, normalize=True)
writer.add_image('Image', x, n_iter)
def load_model(self, filename):
to_load = torch.load(filename)
self.model.load_state_dict(to_load['state_dict'])
if 'optimizer' in to_load.keys():
self.optimizer.load_state_dict(to_load['optimizer'])
if 'scheduler' in to_load.keys():
self.scheduler.load_state_dict(to_load['scheduler'])
return to_load['step']
def load_from_ckpt(self):
'''
load model from existing checkpoints and return the current step
:param ckpt_dir: the directory that stores ckpts
:return: the current starting step
'''
# load from the specified ckpt path
if self.args.ckpt_path != "":
print("Reloading from {}".format(self.args.ckpt_path))
if os.path.isfile(self.args.ckpt_path):
step = self.load_model(self.args.ckpt_path)
else:
raise Exception('no checkpoint found in the following path:{}'.format(self.args.ckpt_path))
else:
ckpt_folder = os.path.join(self.args.outdir, self.args.exp_name)
os.makedirs(ckpt_folder, exist_ok=True)
# load from the most recent ckpt from all existing ckpts
ckpts = [os.path.join(ckpt_folder, f) for f in sorted(os.listdir(ckpt_folder)) if f.endswith('.pth')]
if len(ckpts) > 0:
fpath = ckpts[-1]
step = self.load_model(fpath)
print('Reloading from {}, starting at step={}'.format(fpath, step))
else:
print('No ckpts found, training from scratch...')
step = 0
return step
def save_model(self, step):
ckpt_folder = os.path.join(self.args.outdir, self.args.exp_name)
os.makedirs(ckpt_folder, exist_ok=True)
save_path = os.path.join(ckpt_folder, "{:06d}.pth".format(step))
print('saving ckpts {}...'.format(save_path))
torch.save({'step': step,
'state_dict': self.model.state_dict(),
'optimizer': self.optimizer.state_dict(),
'scheduler': self.scheduler.state_dict(),
},
save_path)
================================================
FILE: CAPS/criterion.py
================================================
import torch
import torch.nn as nn
class CtoFCriterion(nn.Module):
def __init__(self, args):
super(CtoFCriterion, self).__init__()
self.args = args
self.w_ec = args.w_epipolar_coarse
self.w_ef = args.w_epipolar_fine
self.w_cc = args.w_cycle_coarse
self.w_cf = args.w_cycle_coarse
self.w_std = args.w_std
def homogenize(self, coord):
coord = torch.cat((coord, torch.ones_like(coord[:, :, [0]])), -1)
return coord
def set_weight(self, std, mask=None, regularizer=0.0):
if self.args.std:
inverse_std = 1. / torch.clamp(std+regularizer, min=1e-10)
weight = inverse_std / torch.mean(inverse_std)
weight = weight.detach() # Bxn
else:
weight = torch.ones_like(std)
if mask is not None:
weight *= mask.float()
weight /= (torch.mean(weight) + 1e-8)
return weight
def epipolar_cost(self, coord1, coord2, fmatrix):
coord1_h = self.homogenize(coord1).transpose(1, 2)
coord2_h = self.homogenize(coord2).transpose(1, 2)
epipolar_line = fmatrix.bmm(coord1_h) # Bx3xn
epipolar_line_ = epipolar_line / torch.clamp(torch.norm(epipolar_line[:, :2, :], dim=1, keepdim=True), min=1e-8)
essential_cost = torch.abs(torch.sum(coord2_h * epipolar_line_, dim=1)) # Bxn
return essential_cost
def epipolar_loss(self, coord1, coord2, fmatrix, weight):
essential_cost = self.epipolar_cost(coord1, coord2, fmatrix)
loss = torch.mean(weight * essential_cost)
return loss
def cycle_consistency_loss(self, coord1, coord1_loop, weight, th=40):
'''
compute the cycle consistency loss
:param coord1: [batch_size, n_pts, 2]
:param coord1_loop: the predicted location [batch_size, n_pts, 2]
:param weight: the weight [batch_size, n_pts]
:param th: the threshold, only consider distances under this threshold
:return: the cycle consistency loss value
'''
distance = torch.norm(coord1 - coord1_loop, dim=-1)
distance_ = torch.zeros_like(distance)
distance_[distance < th] = distance[distance < th]
loss = torch.mean(weight * distance_)
return loss
def forward(self, coord1, data, fmatrix, pose, im_size):
coord2_ec = data['coord2_ec']
coord2_ef = data['coord2_ef']
coord1_lc = data['coord1_lc']
coord1_lf = data['coord1_lf']
std_c = data['std_c']
std_f = data['std_f']
std_lc = data['std_lc']
std_lf = data['std_lf']
shorter_edge, longer_edge = min(im_size), max(im_size)
epipolar_cost_c = self.epipolar_cost(coord1, coord2_ec, fmatrix)
# only add fine level loss if the coarse level prediction is close enough to gt epipolar line
mask_ctof = (epipolar_cost_c < (shorter_edge * self.args.window_size))
# only add cycle consistency loss if the coarse level prediction is close enough to gt epipolar line
mask_epip_c = (epipolar_cost_c < (shorter_edge * self.args.th_epipolar))
mask_cycle_c = (epipolar_cost_c < (shorter_edge * self.args.th_cycle))
epipolar_cost_f = self.epipolar_cost(coord1, coord2_ef, fmatrix)
# only add cycle consistency loss if the fine level prediction is close enough to gt epipolar line
mask_epip_f = (epipolar_cost_f < (shorter_edge * self.args.th_epipolar))
mask_cycle_f = (epipolar_cost_f < shorter_edge * self.args.th_cycle)
weight_c = self.set_weight(std_c, mask=mask_epip_c)
weight_f = self.set_weight(std_f, mask=mask_epip_f*mask_ctof)
eloss_c = torch.mean(epipolar_cost_c * weight_c) / longer_edge
eloss_f = torch.mean(epipolar_cost_f * weight_f) / longer_edge
weight_cycle_c = self.set_weight(std_c * std_lc, mask=mask_cycle_c)
weight_cycle_f = self.set_weight(std_f * std_lf, mask=mask_cycle_f)
closs_c = self.cycle_consistency_loss(coord1, coord1_lc, weight_cycle_c) / longer_edge
closs_f = self.cycle_consistency_loss(coord1, coord1_lf, weight_cycle_f) / longer_edge
loss = self.w_ec * eloss_c + self.w_ef * eloss_f + self.w_cc * closs_c + self.w_cf * closs_f
std_loss = torch.mean(std_c) + torch.mean(std_f)
loss += self.w_std * std_loss
return loss, eloss_c, eloss_f, closs_c, closs_f, std_loss
================================================
FILE: CAPS/network.py
================================================
import torch
import torch.nn as nn
import torch.nn.functional as F
import importlib
class CAPSNet(nn.Module):
def __init__(self, args, device):
super(CAPSNet, self).__init__()
self.args = args
self.device = device
self.net = ResUNet(pretrained=args.pretrained,
encoder=args.backbone,
coarse_out_ch=args.coarse_feat_dim,
fine_out_ch=args.fine_feat_dim).to(self.device)
@staticmethod
def normalize(coord, h, w):
'''
turn the coordinates from pixel indices to the range of [-1, 1]
:param coord: [..., 2]
:param h: the image height
:param w: the image width
:return: the normalized coordinates [..., 2]
'''
c = torch.Tensor([(w - 1) / 2., (h - 1) / 2.]).to(coord.device).float()
coord_norm = (coord - c) / c
return coord_norm
@staticmethod
def denormalize(coord_norm, h, w):
'''
turn the coordinates from normalized value ([-1, 1]) to actual pixel indices
:param coord_norm: [..., 2]
:param h: the image height
:param w: the image width
:return: actual pixel coordinates
'''
c = torch.Tensor([(w - 1) / 2., (h - 1) / 2.]).to(coord_norm.device)
coord = coord_norm * c + c
return coord
def ind2coord(self, ind, width):
ind = ind.unsqueeze(-1)
x = ind % width
y = ind // width
coord = torch.cat((x, y), -1).float()
return coord
def gen_grid(self, h_min, h_max, w_min, w_max, len_h, len_w):
x, y = torch.meshgrid([torch.linspace(w_min, w_max, len_w), torch.linspace(h_min, h_max, len_h)])
grid = torch.stack((x, y), -1).transpose(0, 1).reshape(-1, 2).float().to(self.device)
return grid
def sample_feat_by_coord(self, x, coord_n, norm=False):
'''
sample from normalized coordinates
:param x: feature map [batch_size, n_dim, h, w]
:param coord_n: normalized coordinates, [batch_size, n_pts, 2]
:param norm: if l2 normalize features
:return: the extracted features, [batch_size, n_pts, n_dim]
'''
feat = F.grid_sample(x, coord_n.unsqueeze(2)).squeeze(-1)
if norm:
feat = F.normalize(feat)
feat = feat.transpose(1, 2)
return feat
def compute_prob(self, feat1, feat2):
'''
compute probability
:param feat1: query features, [batch_size, m, n_dim]
:param feat2: reference features, [batch_size, n, n_dim]
:return: probability, [batch_size, m, n]
'''
assert self.args.prob_from in ['correlation', 'distance']
if self.args.prob_from == 'correlation':
sim = feat1.bmm(feat2.transpose(1, 2))
prob = F.softmax(sim, dim=-1) # Bxmxn
else:
dist = torch.sum(feat1**2, dim=-1, keepdim=True) + \
torch.sum(feat2**2, dim=-1, keepdim=True).transpose(1, 2) - \
2 * feat1.bmm(feat2.transpose(1, 2))
prob = F.softmax(-dist, dim=-1) # Bxmxn
return prob
def get_1nn_coord(self, feat1, featmap2):
'''
find the coordinates of nearest neighbor match
:param feat1: query features, [batch_size, n_pts, n_dim]
:param featmap2: the feature maps of the other image
:return: normalized correspondence locations [batch_size, n_pts, 2]
'''
batch_size, d, h, w = featmap2.shape
feat2_flatten = featmap2.reshape(batch_size, d, h*w).transpose(1, 2) # Bx(hw)xd
assert self.args.prob_from in ['correlation', 'distance']
if self.args.prob_from == 'correlation':
sim = feat1.bmm(feat2_flatten.transpose(1, 2))
ind2_1nn = torch.max(sim, dim=-1)[1]
else:
dist = torch.sum(feat1**2, dim=-1, keepdim=True) + \
torch.sum(feat2_flatten**2, dim=-1, keepdim=True).transpose(1, 2) - \
2 * feat1.bmm(feat2_flatten.transpose(1, 2))
ind2_1nn = torch.min(dist, dim=-1)[1]
coord2 = self.ind2coord(ind2_1nn, w)
coord2_n = self.normalize(coord2, h, w)
return coord2_n
def get_expected_correspondence_locs(self, feat1, featmap2, with_std=False):
'''
compute the expected correspondence locations
:param feat1: the feature vectors of query points [batch_size, n_pts, n_dim]
:param featmap2: the feature maps of the reference image [batch_size, n_dim, h, w]
:param with_std: if return the standard deviation
:return: the normalized expected correspondence locations [batch_size, n_pts, 2]
'''
B, d, h2, w2 = featmap2.size()
grid_n = self.gen_grid(-1, 1, -1, 1, h2, w2)
featmap2_flatten = featmap2.reshape(B, d, h2*w2).transpose(1, 2) # BX(hw)xd
prob = self.compute_prob(feat1, featmap2_flatten) # Bxnx(hw)
grid_n = grid_n.unsqueeze(0).unsqueeze(0) # 1x1x(hw)x2
expected_coord_n = torch.sum(grid_n * prob.unsqueeze(-1), dim=2) # Bxnx2
if with_std:
# convert to normalized scale [-1, 1]
var = torch.sum(grid_n**2 * prob.unsqueeze(-1), dim=2) - expected_coord_n**2 # Bxnx2
std = torch.sum(torch.sqrt(torch.clamp(var, min=1e-10)), -1) # Bxn
return expected_coord_n, std
else:
return expected_coord_n
def get_expected_correspondence_within_window(self, feat1, featmap2, coord2_n, with_std=False):
'''
:param feat1: the feature vectors of query points [batch_size, n_pts, n_dim]
:param featmap2: the feature maps of the reference image [batch_size, n_dim, h, w]
:param coord2_n: normalized center locations [batch_size, n_pts, 2]
:param with_std: if return the standard deviation
:return: the normalized expected correspondence locations, [batch_size, n_pts, 2], optionally with std
'''
batch_size, n_dim, h2, w2 = featmap2.shape
n_pts = coord2_n.shape[1]
grid_n = self.gen_grid(h_min=-self.args.window_size, h_max=self.args.window_size,
w_min=-self.args.window_size, w_max=self.args.window_size,
len_h=int(self.args.window_size*h2), len_w=int(self.args.window_size*w2))
grid_n_ = grid_n.repeat(batch_size, 1, 1, 1) # Bx1xhwx2
coord2_n_grid = coord2_n.unsqueeze(-2) + grid_n_ # Bxnxhwx2
feat2_win = F.grid_sample(featmap2, coord2_n_grid, padding_mode='zeros').permute(0, 2, 3, 1) # Bxnxhwxd
feat1 = feat1.unsqueeze(-2)
prob = self.compute_prob(feat1.reshape(batch_size*n_pts, -1, n_dim),
feat2_win.reshape(batch_size*n_pts, -1, n_dim)).reshape(batch_size, n_pts, -1)
expected_coord2_n = torch.sum(coord2_n_grid * prob.unsqueeze(-1), dim=2) # Bxnx2
if with_std:
var = torch.sum(coord2_n_grid**2 * prob.unsqueeze(-1), dim=2) - expected_coord2_n**2 # Bxnx2
std = torch.sum(torch.sqrt(torch.clamp(var, min=1e-10)), -1) # Bxn
return expected_coord2_n, std
else:
return expected_coord2_n
def forward(self, im1, im2, coord1):
# extract features for both images
xc1, xf1 = self.net(im1)
xc2, xf2 = self.net(im2)
# image width and height
h1i, w1i = im1.size()[2:]
h2i, w2i = im2.size()[2:]
coord1_n = self.normalize(coord1, h1i, w1i)
feat1_coarse = self.sample_feat_by_coord(xc1, coord1_n) # Bxnxd
coord2_ec_n, std_c = self.get_expected_correspondence_locs(feat1_coarse, xc2, with_std=True)
# the center locations of the local window for fine level computation
coord2_ec_n_ = self.get_1nn_coord(feat1_coarse, xc2) if self.args.use_nn else coord2_ec_n
feat1_fine = self.sample_feat_by_coord(xf1, coord1_n) # Bxnxd
coord2_ef_n, std_f = self.get_expected_correspondence_within_window(feat1_fine, xf2,
coord2_ec_n_, with_std=True)
feat2_coarse = self.sample_feat_by_coord(xc2, coord2_ec_n_)
coord1_lc_n, std_lc = self.get_expected_correspondence_locs(feat2_coarse, xc1, with_std=True)
feat2_fine = self.sample_feat_by_coord(xf2, coord2_ef_n) # Bxnxd
coord1_lf_n, std_lf = self.get_expected_correspondence_within_window(feat2_fine, xf1,
coord1_n, with_std=True)
coord2_ec = self.denormalize(coord2_ec_n, h2i, w2i)
coord2_ef = self.denormalize(coord2_ef_n, h2i, w2i)
coord1_lc = self.denormalize(coord1_lc_n, h1i, w1i)
coord1_lf = self.denormalize(coord1_lf_n, h1i, w1i)
return {'coord2_ec': coord2_ec, 'coord2_ef': coord2_ef,
'coord1_lc': coord1_lc, 'coord1_lf': coord1_lf,
'std_c': std_c, 'std_f': std_f,
'std_lc': std_lc, 'std_lf': std_lf,
}
def extract_features(self, im, coord):
'''
extract coarse and fine level features given the input image and 2d locations
:param im: [batch_size, 3, h, w]
:param coord: [batch_size, n_pts, 2]
:return: coarse features [batch_size, n_pts, coarse_feat_dim] and fine features [batch_size, n_pts, fine_feat_dim]
'''
xc, xf = self.net(im)
hi, wi = im.size()[2:]
coord_n = self.normalize(coord, hi, wi)
feat_c = self.sample_feat_by_coord(xc, coord_n)
feat_f = self.sample_feat_by_coord(xf, coord_n)
return feat_c, feat_f
def test(self, im1, im2, coord1):
'''
given a pair of images im1, im2, compute the coorrespondences for query points coord1.
We performa full image search at coarse level and local search at fine level
:param im1: [batch_size, 3, h, w]
:param im2: [batch_size, 3, h, w]
:param coord1: [batch_size, n_pts, 2]
:return: the fine level correspondence location [batch_size, n_pts, 2]
'''
xc1, xf1 = self.net(im1)
xc2, xf2 = self.net(im2)
h1i, w1i = im1.shape[2:]
h2i, w2i = im2.shape[2:]
coord1_n = self.normalize(coord1, h1i, w1i)
feat1_c = self.sample_feat_by_coord(xc1, coord1_n)
_, std_c = self.get_expected_correspondence_locs(feat1_c, xc2, with_std=True)
coord2_ec_n = self.get_1nn_coord(feat1_c, xc2)
feat1_f = self.sample_feat_by_coord(xf1, coord1_n)
_, std_f = self.get_expected_correspondence_within_window(feat1_f, xf2, coord2_ec_n, with_std=True)
coord2_ef_n = self.get_1nn_coord(feat1_f, xf2)
coord2_ef = self.denormalize(coord2_ef_n, h2i, w2i)
std = (std_c + std_f)/2
return coord2_ef, std
####################### ResUnet ##########################
def class_for_name(module_name, class_name):
# load the module, will raise ImportError if module cannot be loaded
m = importlib.import_module(module_name)
return getattr(m, class_name)
class conv(nn.Module):
def __init__(self, num_in_layers, num_out_layers, kernel_size, stride):
super(conv, self).__init__()
self.kernel_size = kernel_size
self.conv = nn.Conv2d(num_in_layers,
num_out_layers,
kernel_size=kernel_size,
stride=stride,
padding=(self.kernel_size - 1) // 2)
self.bn = nn.BatchNorm2d(num_out_layers)
def forward(self, x):
return F.elu(self.bn(self.conv(x)), inplace=True)
class upconv(nn.Module):
def __init__(self, num_in_layers, num_out_layers, kernel_size, scale):
super(upconv, self).__init__()
self.scale = scale
self.conv = conv(num_in_layers, num_out_layers, kernel_size, 1)
def forward(self, x):
x = nn.functional.interpolate(x, scale_factor=self.scale, align_corners=True, mode='bilinear')
return self.conv(x)
class ResUNet(nn.Module):
def __init__(self,
encoder='resnet50',
pretrained=True,
coarse_out_ch=128,
fine_out_ch=128
):
super(ResUNet, self).__init__()
assert encoder in ['resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152'], "Incorrect encoder type"
if encoder in ['resnet18', 'resnet34']:
filters = [64, 128, 256, 512]
else:
filters = [256, 512, 1024, 2048]
resnet = class_for_name("torchvision.models", encoder)(pretrained=pretrained)
self.firstconv = resnet.conv1 # H/2
self.firstbn = resnet.bn1
self.firstrelu = resnet.relu
self.firstmaxpool = resnet.maxpool # H/4
# encoder
self.layer1 = resnet.layer1 # H/4
self.layer2 = resnet.layer2 # H/8
self.layer3 = resnet.layer3 # H/16
# coarse-level conv
self.conv_coarse = conv(filters[2], coarse_out_ch, 1, 1)
# decoder
self.upconv3 = upconv(filters[2], 512, 3, 2)
self.iconv3 = conv(filters[1] + 512, 512, 3, 1)
self.upconv2 = upconv(512, 256, 3, 2)
self.iconv2 = conv(filters[0] + 256, 256, 3, 1)
# fine-level conv
self.conv_fine = conv(256, fine_out_ch, 1, 1)
def skipconnect(self, x1, x2):
diffY = x2.size()[2] - x1.size()[2]
diffX = x2.size()[3] - x1.size()[3]
x1 = F.pad(x1, (diffX // 2, diffX - diffX // 2,
diffY // 2, diffY - diffY // 2))
# for padding issues, see
# https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a
# https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd
x = torch.cat([x2, x1], dim=1)
return x
def forward(self, x):
x = self.firstrelu(self.firstbn(self.firstconv(x)))
x = self.firstmaxpool(x)
x1 = self.layer1(x)
x2 = self.layer2(x1)
x3 = self.layer3(x2)
x_coarse = self.conv_coarse(x3)
x = self.upconv3(x3)
x = self.skipconnect(x2, x)
x = self.iconv3(x)
x = self.upconv2(x)
x = self.skipconnect(x1, x)
x = self.iconv2(x)
x_fine = self.conv_fine(x)
return [x_coarse, x_fine]
================================================
FILE: LICENSE
================================================
Copyright 2021 Qianqian Wang
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# Learning Feature Descriptors using Camera Pose Supervision
This repository contains a PyTorch implementation of the paper:
[*Learning Feature Descriptors using Camera Pose Supervision*](https://qianqianwang68.github.io/CAPS/)
[[Project page]](https://qianqianwang68.github.io/CAPS/)
[[Arxiv]](https://arxiv.org/abs/2004.13324)
[Qianqian Wang](https://www.cs.cornell.edu/~qqw/),
[Xiaowei Zhou](http://www.cad.zju.edu.cn/home/xzhou/),
[Bharath Hariharan](http://home.bharathh.info/),
[Noah Snavely](http://www.cs.cornell.edu/~snavely/)
ECCV 2020 (*Oral*)

## Abstract
Recent research on learned visual descriptors has shown promising improvements in correspondence estimation, a key component of many 3D vision tasks. However, existing descriptor learning frameworks typically require ground-truth correspondences between feature points for training, which are challenging to acquire at scale. In this paper we propose a novel weakly-supervised framework that can learn feature descriptors solely from relative camera poses between images. To do so, we devise both a new loss function that exploits the epipolar constraint given by camera poses, and a new model architecture that makes the whole pipeline differentiable and efficient. Because we no longer need pixel-level ground-truth correspondences, our framework opens up the possibility of training on much larger and more diverse datasets for better and unbiased descriptors. We call the resulting descriptors CAmera Pose Supervised, or CAPS, descriptors. Though trained with weak supervision, CAPS descriptors outperform even prior fully-supervised descriptors and achieve state-of-the-art performance on a variety of geometric tasks.
## Requirements
```bash
# Create conda environment with torch 1.0.1 and CUDA 10.0
conda env create -f environment.yml
conda activate caps
```
If you encounter problems with OpenCV, try to uninstall your current opencv packages and reinstall them again
```bash
pip uninstall opencv-python
pip uninstall opencv-contrib-python
pip install opencv-python==3.4.2.17
pip install opencv-contrib-python==3.4.2.17
```
## Pretrained Model
Pretrained model can be downloaded using this google drive [link](https://drive.google.com/file/d/1KfIQYyM7vlSvvQ3X7xi1YSPrietqeXYd/view?usp=sharing), or this BaiduYun [link](https://pan.baidu.com/s/1rGt4okK3KsumPLUVhOJdXQ) (password: 3na7).
## Dataset
Please download the preprocessed MegaDepth dataset using this google drive [link](https://drive.google.com/file/d/1Xp6BRKx_5sIwdJWVK0kJTmtUcQNMY5yL/view?usp=sharing) or this BaiduYun [link](https://pan.baidu.com/s/1rGt4okK3KsumPLUVhOJdXQ) (password: 3na7
## Training
To start training, please download our [training data](https://drive.google.com/file/d/1Xp6BRKx_5sIwdJWVK0kJTmtUcQNMY5yL/view?usp=sharing), and run the following command:
```bash
# example usage
python train.py --config configs/train_megadepth.yaml
```
## Feature extraction
We provide code for extracting CAPS descriptors on HPatches dataset.
To download and use the HPatches Sequences, please refer to this [link](https://github.com/mihaidusmanu/d2-net/tree/master/hpatches_sequences).
To extract CAPS features on HPatches dataset, download the pretrained model, modify paths in ```configs/extract_features_hpatches.yaml``` and run
```bash
python extract_features.py --config configs/extract_features_hpatches.yaml
```
## Interactive demo
We provide an interactive demo where you could click on locations in the first image and see their predicted correspondences in the second image.
Please refer to ```jupyter/visualization.ipynb``` for more details.
## Cite
Please cite our work if you find it useful:
```bibtex
@inproceedings{wang2020learning,
Title = {Learning Feature Descriptors using Camera Pose Supervision},
Author = {Qianqian Wang and Xiaowei Zhou and Bharath Hariharan and Noah Snavely},
booktitle = {Proc. European Conference on Computer Vision (ECCV)},
Year = {2020},
}
```
**Acknowledgements**. We thank Kai Zhang, Zixin Luo, Zhengqi Li for helpful discussion and comments. This work was partly supported by a DARPA
LwLL grant, and in part by the generosity of Eric and Wendy Schmidt by
recommendation of the Schmidt Futures program.
================================================
FILE: config.py
================================================
import configargparse
def get_args():
parser = configargparse.ArgParser(config_file_parser_class=configargparse.YAMLConfigFileParser)
parser.add_argument('--config', is_config_file=True, help='config file path')
## path options
parser.add_argument('--datadir', type=str, help='the dataset directory')
parser.add_argument("--logdir", type=str, default='./logs/', help='dir of tensorboard logs')
parser.add_argument("--outdir", type=str, default='./out/', help='dir of output e.g., ckpts')
parser.add_argument("--ckpt_path", type=str, default="",
help='specific checkpoint path to load the model from, '
'if not specified, automatically reload from most recent checkpoints')
## general options
parser.add_argument("--exp_name", type=str, help='experiment name')
parser.add_argument('--n_iters', type=int, default=200000, help='max number of training iterations')
parser.add_argument('--phase', type=str, default='train', help='train/val/test')
# data options
parser.add_argument('--workers', type=int, help='number of data loading workers', default=4)
parser.add_argument('--num_pts', type=int, default=500, help='num of points trained in each pair')
parser.add_argument('--train_kp', type=str, default='mixed', help='sift/random/mixed')
parser.add_argument('--prune_kp', type=int, default=1, help='if prune non-matchable keypoints')
# training options
parser.add_argument('--batch_size', type=int, default=6, help='input batch size')
parser.add_argument('--lr', type=float, default=1e-4, help='base learning rate')
parser.add_argument("--lrate_decay_steps", type=int, default=80000,
help='decay learning rate by a factor every specified number of steps')
parser.add_argument("--lrate_decay_factor", type=float, default=0.5,
help='decay learning rate by a factor every specified number of steps')
## model options
parser.add_argument('--backbone', type=str, default='resnet50',
help='backbone for feature representation extraction. supported: resent')
parser.add_argument('--pretrained', type=int, default=1,
help='if use ImageNet pretrained weights to initialize the network')
parser.add_argument('--coarse_feat_dim', type=int, default=128,
help='the feature dimension for coarse level features')
parser.add_argument('--fine_feat_dim', type=int, default=128,
help='the feature dimension for fine level features')
parser.add_argument('--prob_from', type=str, default='correlation',
help='compute prob by softmax(correlation score), or softmax(-distance),'
'options: correlation|distance')
parser.add_argument('--window_size', type=float, default=0.125,
help='the size of the window, w.r.t image width at the fine level')
parser.add_argument('--use_nn', type=int, default=1, help='if use nearest neighbor in the coarse level')
## loss function options
parser.add_argument('--std', type=int, default=1, help='reweight loss using the standard deviation')
parser.add_argument('--w_epipolar_coarse', type=float, default=1, help='coarse level epipolar loss weight')
parser.add_argument('--w_epipolar_fine', type=float, default=1, help='fine level epipolar loss weight')
parser.add_argument('--w_cycle_coarse', type=float, default=0.1, help='coarse level cycle consistency loss weight')
parser.add_argument('--w_cycle_fine', type=float, default=0.1, help='fine level cycle consistency loss weight')
parser.add_argument('--w_std', type=float, default=0, help='the weight for the loss on std')
parser.add_argument('--th_cycle', type=float, default=0.025,
help='if the distance (normalized scale) from the prediction to epipolar line > this th, '
'do not add the cycle consistency loss')
parser.add_argument('--th_epipolar', type=float, default=0.5,
help='if the distance (normalized scale) from the prediction to epipolar line > this th, '
'do not add the epipolar loss')
## logging options
parser.add_argument('--log_scalar_interval', type=int, default=20, help='print interval')
parser.add_argument('--log_img_interval', type=int, default=500, help='log image interval')
parser.add_argument("--save_interval", type=int, default=10000, help='frequency of weight ckpt saving')
## eval options
parser.add_argument('--extract_img_dir', type=str, help='the directory of images to extract features')
parser.add_argument('--extract_out_dir', type=str, help='the directory of images to extract features')
args = parser.parse_known_args()[0]
return args
================================================
FILE: configs/extract_features_hpatches.yaml
================================================
# extract feature descriptors using pretrained model weights
expname: feature_extraction
# replace the following with your pretrained model path, img_dir and out_dir, respectively
ckpt_path: 'pretrained/caps-pretrained.pth'
extract_img_dir: '/phoenix/S7/qw246/hpatches-benchmark/data/d2net/hpatches-sequences-release/'
extract_out_dir: '/phoenix/S7/qw246/hpatches-benchmark/data/output/desc/caps/'
================================================
FILE: configs/train_megadepth.yaml
================================================
# training from scratch using a single gpu, default configs in config.py file
exp_name: train_caps
datadir: /phoenix/S7/qw246/CAPS-MegaDepth-release-light # replace with your data dir
================================================
FILE: dataloader/__init__.py
================================================
================================================
FILE: dataloader/data_utils.py
================================================
import numpy as np
import cv2
def skew(x):
return np.array([[0, -x[2], x[1]],
[x[2], 0, -x[0]],
[-x[1], x[0], 0]])
def rotateImage(image, angle):
h, w = image.shape[:2]
angle_radius = np.abs(angle / 180. * np.pi)
cos = np.cos(angle_radius)
sin = np.sin(angle_radius)
tan = np.tan(angle_radius)
scale_h = (h / cos + (w - h * tan) * sin) / h
scale_w = (h / sin + (w - h / tan) * cos) / w
scale = max(scale_h, scale_w)
image_center = tuple(np.array(image.shape[1::-1]) / 2.)
rot_mat = cv2.getRotationMatrix2D(image_center, angle, scale)
result = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR)
rotation = np.eye(4)
rotation[:2, :2] = rot_mat[:2, :2]
return result, rotation
def perspective_transform(img, param=0.001):
h, w = img.shape[:2]
random_state = np.random.RandomState(None)
M = np.array([[1 - param + 2 * param * random_state.rand(),
-param + 2 * param * random_state.rand(),
-param + 2 * param * random_state.rand()],
[-param + 2 * param * random_state.rand(),
1 - param + 2 * param * random_state.rand(),
-param + 2 * param * random_state.rand()],
[-param + 2 * param * random_state.rand(),
-param + 2 * param * random_state.rand(),
1 - param + 2 * param * random_state.rand()]])
dst = cv2.warpPerspective(img, M, (w, h))
return dst, M
def generate_query_kpts(img, mode, num_pts, h, w):
# generate candidate query points
if mode == 'random':
kp1_x = np.random.rand(num_pts) * (w - 1)
kp1_y = np.random.rand(num_pts) * (h - 1)
coord = np.stack((kp1_x, kp1_y)).T
elif mode == 'sift':
gray1 = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
sift = cv2.xfeatures2d.SIFT_create(nfeatures=num_pts)
kp1 = sift.detect(gray1)
coord = np.array([[kp.pt[0], kp.pt[1]] for kp in kp1])
elif mode == 'mixed':
kp1_x = np.random.rand(1 * int(0.1 * num_pts)) * (w - 1)
kp1_y = np.random.rand(1 * int(0.1 * num_pts)) * (h - 1)
kp1_rand = np.stack((kp1_x, kp1_y)).T
sift = cv2.xfeatures2d.SIFT_create(nfeatures=int(0.9 * num_pts))
gray1 = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
kp1_sift = sift.detect(gray1)
kp1_sift = np.array([[kp.pt[0], kp.pt[1]] for kp in kp1_sift])
if len(kp1_sift) == 0:
coord = kp1_rand
else:
coord = np.concatenate((kp1_rand, kp1_sift), 0)
else:
raise Exception('unknown type of keypoints')
return coord
def prune_kpts(coord1, F_gt, im2_size, intrinsic1, intrinsic2, pose, d_min, d_max):
# compute the epipolar lines corresponding to coord1
coord1_h = np.concatenate([coord1, np.ones_like(coord1[:, [0]])], axis=1).T # 3xn
epipolar_line = F_gt.dot(coord1_h) # 3xn
epipolar_line /= np.clip(np.linalg.norm(epipolar_line[:2], axis=0), a_min=1e-10, a_max=None) # 3xn
# determine whether the epipolar lines intersect with the second image
h2, w2 = im2_size
corners = np.array([[0, 0, 1], [0, h2 - 1, 1], [w2 - 1, 0, 1], [w2 - 1, h2 - 1, 1]]) # 4x3
dists = np.abs(corners.dot(epipolar_line))
# if the epipolar line is far away from any image corners than sqrt(h^2+w^2)
# it doesn't intersect with the image
non_intersect = (dists > np.sqrt(w2 ** 2 + h2 ** 2)).any(axis=0)
# determine if points in coord1 is likely to have correspondence in the other image by the rough depth range
intrinsic1_4x4 = np.eye(4)
intrinsic1_4x4[:3, :3] = intrinsic1
intrinsic2_4x4 = np.eye(4)
intrinsic2_4x4[:3, :3] = intrinsic2
coord1_h_min = np.concatenate([d_min * coord1,
d_min * np.ones_like(coord1[:, [0]]),
np.ones_like(coord1[:, [0]])], axis=1).T
coord1_h_max = np.concatenate([d_max * coord1,
d_max * np.ones_like(coord1[:, [0]]),
np.ones_like(coord1[:, [0]])], axis=1).T
coord2_h_min = intrinsic2_4x4.dot(pose).dot(np.linalg.inv(intrinsic1_4x4)).dot(coord1_h_min)
coord2_h_max = intrinsic2_4x4.dot(pose).dot(np.linalg.inv(intrinsic1_4x4)).dot(coord1_h_max)
coord2_min = coord2_h_min[:2] / (coord1_h_min[2] + 1e-10)
coord2_max = coord2_h_max[:2] / (coord1_h_max[2] + 1e-10)
out_range = ((coord2_min[0] < 0) & (coord2_max[0] < 0)) | \
((coord2_min[1] < 0) & (coord2_max[1] < 0)) | \
((coord2_min[0] > w2 - 1) & (coord2_max[0] > w2 - 1)) | \
((coord2_min[1] > h2 - 1) & (coord2_max[1] > h2 - 1))
ind_intersect = ~(non_intersect | out_range)
return ind_intersect
================================================
FILE: dataloader/megadepth.py
================================================
import torch
from torch.utils.data import Dataset
import os
import numpy as np
import cv2
import skimage.io as io
import torchvision.transforms as transforms
import utils
import collections
from tqdm import tqdm
import dataloader.data_utils as data_utils
rand = np.random.RandomState(234)
class MegaDepthLoader():
def __init__(self, args):
self.args = args
self.dataset = MegaDepth(args)
self.data_loader = torch.utils.data.DataLoader(self.dataset, batch_size=args.batch_size, shuffle=False,
num_workers=args.workers, collate_fn=self.my_collate)
def my_collate(self, batch):
''' Puts each data field into a tensor with outer dimension batch size '''
batch = list(filter(lambda b: b is not None, batch))
return torch.utils.data.dataloader.default_collate(batch)
def load_data(self):
return self.data_loader
def name(self):
return 'MegaDepthLoader'
def __len__(self):
return len(self.dataset)
class MegaDepth(Dataset):
def __init__(self, args):
self.args = args
if args.phase == 'train':
# augment during training
self.transform = transforms.Compose([transforms.ToPILImage(),
transforms.ColorJitter
(brightness=1, contrast=1, saturation=1, hue=0.4),
transforms.ToTensor(),
transforms.Normalize(mean=(0.485, 0.456, 0.406),
std=(0.229, 0.224, 0.225)),
])
else:
self.transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize(mean=(0.485, 0.456, 0.406),
std=(0.229, 0.224, 0.225)),
])
self.phase = args.phase
self.root = os.path.join(args.datadir, self.phase)
self.images = self.read_img_cam()
self.imf1s, self.imf2s = self.read_pairs()
print('total number of image pairs loaded: {}'.format(len(self.imf1s)))
# shuffle data
index = np.arange(len(self.imf1s))
rand.shuffle(index)
self.imf1s = list(np.array(self.imf1s)[index])
self.imf2s = list(np.array(self.imf2s)[index])
def read_img_cam(self):
images = {}
Image = collections.namedtuple(
"Image", ["name", "w", "h", "fx", "fy", "cx", "cy", "rvec", "tvec"])
for scene_id in os.listdir(self.root):
densefs = [f for f in os.listdir(os.path.join(self.root, scene_id))
if 'dense' in f and os.path.isdir(os.path.join(self.root, scene_id, f))]
for densef in densefs:
folder = os.path.join(self.root, scene_id, densef, 'aligned')
img_cam_txt_path = os.path.join(folder, 'img_cam.txt')
with open(img_cam_txt_path, "r") as fid:
while True:
line = fid.readline()
if not line:
break
line = line.strip()
if len(line) > 0 and line[0] != "#":
elems = line.split()
image_name = elems[0]
img_path = os.path.join(folder, 'images', image_name)
w, h = int(elems[1]), int(elems[2])
fx, fy = float(elems[3]), float(elems[4])
cx, cy = float(elems[5]), float(elems[6])
R = np.array(elems[7:16])
T = np.array(elems[16:19])
images[img_path] = Image(
name=image_name, w=w, h=h, fx=fx, fy=fy, cx=cx, cy=cy, rvec=R, tvec=T
)
return images
def read_pairs(self):
imf1s, imf2s = [], []
print('reading image pairs from {}...'.format(self.root))
for scene_id in tqdm(os.listdir(self.root), desc='# loading data from scene folders'):
densefs = [f for f in os.listdir(os.path.join(self.root, scene_id))
if 'dense' in f and os.path.isdir(os.path.join(self.root, scene_id, f))]
for densef in densefs:
imf1s_ = []
imf2s_ = []
folder = os.path.join(self.root, scene_id, densef, 'aligned')
pairf = os.path.join(folder, 'pairs.txt')
if os.path.exists(pairf):
f = open(pairf, 'r')
for line in f:
imf1, imf2 = line.strip().split(' ')
imf1s_.append(os.path.join(folder, 'images', imf1))
imf2s_.append(os.path.join(folder, 'images', imf2))
# make # image pairs per scene more balanced
if len(imf1s_) > 5000:
index = np.arange(len(imf1s_))
rand.shuffle(index)
imf1s_ = list(np.array(imf1s_)[index[:5000]])
imf2s_ = list(np.array(imf2s_)[index[:5000]])
imf1s.extend(imf1s_)
imf2s.extend(imf2s_)
return imf1s, imf2s
@staticmethod
def get_intrinsics(im_meta):
return np.array([[im_meta.fx, 0, im_meta.cx],
[0, im_meta.fy, im_meta.cy],
[0, 0, 1]])
@staticmethod
def get_extrinsics(im_meta):
R = im_meta.rvec.reshape(3, 3)
t = im_meta.tvec
extrinsic = np.eye(4)
extrinsic[:3, :3] = R
extrinsic[:3, 3] = t
return extrinsic
def __getitem__(self, item):
imf1 = self.imf1s[item]
imf2 = self.imf2s[item]
im1_meta = self.images[imf1]
im2_meta = self.images[imf2]
im1 = io.imread(imf1)
im2 = io.imread(imf2)
h, w = im1.shape[:2]
intrinsic1 = self.get_intrinsics(im1_meta)
intrinsic2 = self.get_intrinsics(im2_meta)
extrinsic1 = self.get_extrinsics(im1_meta)
extrinsic2 = self.get_extrinsics(im2_meta)
relative = extrinsic2.dot(np.linalg.inv(extrinsic1))
R = relative[:3, :3]
# remove pairs that have a relative rotation angle larger than 80 degrees
theta = np.arccos(np.clip((np.trace(R) - 1) / 2, -1, 1)) * 180 / np.pi
if theta > 80 and self.phase == 'train':
return None
T = relative[:3, 3]
tx = data_utils.skew(T)
E_gt = np.dot(tx, R)
F_gt = np.linalg.inv(intrinsic2).T.dot(E_gt).dot(np.linalg.inv(intrinsic1))
# generate candidate query points
coord1 = data_utils.generate_query_kpts(im1, self.args.train_kp, 10*self.args.num_pts, h, w)
# if no keypoints are detected
if len(coord1) == 0:
return None
# prune query keypoints that are not likely to have correspondence in the other image
if self.args.prune_kp:
ind_intersect = data_utils.prune_kpts(coord1, F_gt, im2.shape[:2], intrinsic1, intrinsic2,
relative, d_min=4, d_max=400)
if np.sum(ind_intersect) == 0:
return None
coord1 = coord1[ind_intersect]
coord1 = utils.random_choice(coord1, self.args.num_pts)
coord1 = torch.from_numpy(coord1).float()
im1_ori, im2_ori = torch.from_numpy(im1), torch.from_numpy(im2)
F_gt = torch.from_numpy(F_gt).float() / (F_gt[-1, -1] + 1e-10)
intrinsic1 = torch.from_numpy(intrinsic1).float()
intrinsic2 = torch.from_numpy(intrinsic2).float()
pose = torch.from_numpy(relative[:3, :]).float()
im1_tensor = self.transform(im1)
im2_tensor = self.transform(im2)
out = {'im1': im1_tensor,
'im2': im2_tensor,
'im1_ori': im1_ori,
'im2_ori': im2_ori,
'pose': pose,
'F': F_gt,
'intrinsic1': intrinsic1,
'intrinsic2': intrinsic2,
'coord1': coord1}
return out
def __len__(self):
return len(self.imf1s)
================================================
FILE: environment.yml
================================================
name: caps
channels:
- pytorch
- defaults
dependencies:
- _libgcc_mutex=0.1=main
- blas=1.0=mkl
- ca-certificates=2020.7.22=0
- certifi=2020.6.20=py37_0
- cffi=1.14.1=py37he30daa8_0
- cudatoolkit=10.0.130=0
- freetype=2.10.2=h5ab3b9f_0
- intel-openmp=2020.1=217
- jpeg=9b=h024ee3a_2
- lcms2=2.11=h396b838_0
- ld_impl_linux-64=2.33.1=h53a641e_7
- libedit=3.1.20191231=h14c3975_1
- libffi=3.3=he6710b0_2
- libgcc-ng=9.1.0=hdf63c60_0
- libpng=1.6.37=hbc83047_0
- libstdcxx-ng=9.1.0=hdf63c60_0
- libtiff=4.1.0=h2733197_1
- lz4-c=1.9.2=he6710b0_1
- mkl=2020.1=217
- mkl-service=2.3.0=py37he904b0f_0
- mkl_fft=1.1.0=py37h23d657b_0
- mkl_random=1.1.1=py37h0573a6f_0
- ncurses=6.2=he6710b0_1
- ninja=1.10.0=py37hfd86e86_0
- numpy=1.19.1=py37hbc911f0_0
- numpy-base=1.19.1=py37hfa32c7d_0
- olefile=0.46=py37_0
- openssl=1.1.1g=h7b6447c_0
- pillow=7.2.0=py37hb39fc2d_0
- pip=20.2.2=py37_0
- pycparser=2.20=py_2
- python=3.7.7=hcff3b4d_5
- pytorch=1.0.1=py3.7_cuda10.0.130_cudnn7.4.2_2
- readline=8.0=h7b6447c_0
- setuptools=49.6.0=py37_0
- six=1.15.0=py_0
- sqlite=3.33.0=h62c20be_0
- tk=8.6.10=hbc83047_0
- torchvision=0.2.2=py_3
- wheel=0.34.2=py37_0
- xz=5.2.5=h7b6447c_0
- zlib=1.2.11=h7b6447c_3
- zstd=1.4.5=h9ceee32_0
- pip:
- chardet==3.0.4
- configargparse==1.2.3
- cycler==0.10.0
- decorator==4.4.2
- filelock==3.0.12
- gdown==3.12.2
- idna==2.10
- imageio==2.9.0
- kiwisolver==1.2.0
- matplotlib==3.3.1
- networkx==2.5
- opencv-contrib-python==3.4.2.17
- opencv-python==3.4.2.17
- protobuf==3.13.0
- pyparsing==2.4.7
- pysocks==1.7.1
- python-dateutil==2.8.1
- pywavelets==1.1.1
- pyyaml==5.3.1
- requests==2.24.0
- scikit-image==0.15.0
- scipy==1.3.1
- tensorboardx==2.1
- tqdm==4.48.2
- urllib3==1.25.10
prefix: /phoenix/S7/qw246/anaconda3/envs/caps
================================================
FILE: extract_features.py
================================================
import torch
from torch.utils.data import Dataset
import os
import numpy as np
import cv2
import skimage.io as io
import torchvision.transforms as transforms
import config
from tqdm import tqdm
from CAPS.caps_model import CAPSModel
class HPatchDataset(Dataset):
def __init__(self, imdir):
self.transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize(mean=(0.485, 0.456, 0.406),
std=(0.229, 0.224, 0.225)),
])
self.imfs = []
for f in os.listdir(imdir):
scene_dir = os.path.join(imdir, f)
self.imfs.extend([os.path.join(scene_dir, '{}.ppm').format(ind) for ind in range(1, 7)])
def __getitem__(self, item):
imf = self.imfs[item]
im = io.imread(imf)
im_tensor = self.transform(im)
# using sift keypoints
sift = cv2.xfeatures2d.SIFT_create()
gray = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
kpts = sift.detect(gray)
kpts = np.array([[kp.pt[0], kp.pt[1]] for kp in kpts])
coord = torch.from_numpy(kpts).float()
out = {'im': im_tensor, 'coord': coord, 'imf': imf}
return out
def __len__(self):
return len(self.imfs)
if __name__ == '__main__':
# example code for extracting features for HPatches dataset, SIFT keypoint is used
args = config.get_args()
device = 'cuda' if torch.cuda.is_available() else 'cpu'
dataset = HPatchDataset(args.extract_img_dir)
data_loader = torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, num_workers=args.workers)
model = CAPSModel(args)
outdir = args.extract_out_dir
os.makedirs(outdir, exist_ok=True)
with torch.no_grad():
for data in tqdm(data_loader):
im = data['im'].to(device)
img_path = data['imf'][0]
coord = data['coord'].to(device)
feat_c, feat_f = model.extract_features(im, coord)
desc = torch.cat((feat_c, feat_f), -1).squeeze(0).detach().cpu().numpy()
kpt = coord.cpu().numpy().squeeze(0)
out_path = os.path.join(outdir, '{}-{}'.format(os.path.basename(os.path.dirname(img_path)),
os.path.basename(img_path),
))
with open(out_path + '.caps', 'wb') as output_file:
np.savez(
output_file,
keypoints=kpt,
scores=[],
descriptors=desc
)
================================================
FILE: jupyter/functions.py
================================================
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import torch
import sys
sys.path.append('../')
from dataloader import megadepth
import torch.utils.data
from CAPS.caps_model import CAPSModel
import cv2
class Visualization(object):
def __init__(self, args):
dataset = megadepth.MegaDepth(args)
self.dataloader = torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=True, num_workers=1)
self.model = CAPSModel(args)
self.loader_iter = iter(self.dataloader)
def random_sample(self):
self.sample = next(self.loader_iter)
def plot_img_pair(self, with_std=False, with_epipline=False):
self.coords = []
self.colors = []
self.with_std = with_std
self.with_epipline = with_epipline
im1 = self.sample['im1_ori']
im2 = self.sample['im2_ori']
self.h, self.w = im1.shape[1], im1.shape[2]
im1 = im1.squeeze().cpu().numpy()
im2 = im2.squeeze().cpu().numpy()
blank = np.ones((self.h, 5, 3)) * 255
out = np.concatenate((im1, blank, im2), 1).astype(np.uint8)
self.fig = plt.figure(figsize=(12, 5))
self.ax = self.fig.add_subplot(111)
self.ax.imshow(out)
self.ax.axis('off')
plt.tight_layout()
cid = self.fig.canvas.mpl_connect('button_press_event', self.onclick)
def onclick(self, event):
color = tuple(np.random.rand(3).tolist())
coord = [event.xdata, event.ydata]
self.coord = coord
self.color = color
self.coords.append(coord)
self.colors.append(color)
self.ax.scatter(event.xdata, event.ydata, c=color)
self.find_correspondence()
self.plot_correspondence()
def find_correspondence(self):
data_in = self.sample
data_in['coord1'] = torch.from_numpy(np.array(self.coord)).float().cuda().unsqueeze(0).unsqueeze(0)
data_in['coord2'] = data_in['coord1']
self.model.set_input(data_in)
coord2_e, std = self.model.test()
self.correspondence = coord2_e.squeeze().cpu().numpy()
self.std = std.squeeze().cpu().numpy()
def plot_correspondence(self):
point1 = self.coord
point2 = self.correspondence
point2[0] += self.w + 5
self.ax.scatter(point2[0], point2[1], color=self.color)
if self.with_std:
circle = plt.Circle((point2[0], point2[1]), radius=100 * self.std, fill=False, color=self.color)
self.ax.add_patch(circle)
if self.with_epipline:
line2 = cv2.computeCorrespondEpilines(np.array(point1).reshape(-1, 1, 2), 1,
self.sample['F'].squeeze().cpu().numpy())
line2 = np.array(line2).squeeze()
intersection = np.array([[0, -line2[2]/line2[1]],
[-line2[2]/line2[0], 0],
[self.w-1, -(line2[2]+line2[0]*(self.w-1))/line2[1]],
[-(line2[1]*(self.h-1)+line2[2])/line2[0], self.h-1]])
valid = (intersection[:, 0] >= 0) & (intersection[:, 0] <= self.w-1) & \
(intersection[:, 1] >= 0) & (intersection[:, 1] <= self.h-1)
if np.sum(valid) == 2:
intersection = intersection[valid].astype(int)
x0, y0 = intersection[0]
x1, y1 = intersection[1]
l = mlines.Line2D([x0+self.w + 5, x1+self.w + 5], [y0, y1], color=self.color)
self.ax.add_line(l)
plt.show()
================================================
FILE: jupyter/visualization.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Interactive demo\n",
"\n",
"This interactive demo shows the correspondences our method obtain by **densely** searching in the image space.\n",
"\n",
"\n",
"Run the first code block and wait for it to complete ..."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The autoreload extension is already loaded. To reload it, use:\n",
" %reload_ext autoreload\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\r",
"# loading data from scene folders: 0%| | 0/37 [00:00<?, ?it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"reading image pairs from /phoenix/S7/qw246/CAPS-MegaDepth-release-light/test...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"# loading data from scene folders: 100%|██████████| 37/37 [00:19<00:00, 1.88it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"total number of image pairs loaded: 216892\n",
"Reloading from /phoenix/S7/qw246/caps-internal/pretrained/caps-pretrained.pth\n"
]
}
],
"source": [
"%load_ext autoreload\n",
"%autoreload 2\n",
"from functions import Visualization\n",
"import sys\n",
"sys.path.append('../')\n",
"import config\n",
"args = config.get_args()\n",
"args.datadir = '/phoenix/S7/qw246/CAPS-MegaDepth-release-light' # replace this with your data directory\n",
"args.ckpt_path = '/phoenix/S7/qw246/caps-internal/pretrained/caps-pretrained.pth' # replace this with your path of pretrained model\n",
"args.phase = 'test'\n",
"sample = Visualization(args)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Run the second code block. When it's done, click on anywhere you want in the **left image**, and check out the predicted correspondence in the **right image**.\n",
"Note that all of image pairs are from the held-out test set.\n",
"\n",
"**Note**:\n",
"1. To view **another random example**, simply **rerun** the following code block.\n",
"2. The circle for each predicted point indicates the **standard deviation**. The larger the radius is, the more uncertain the prediction is. \n",
"3. The straight line is the corresponding **groundtruth epipolar line**.\n",
"4. To turn off std or epipolar line, set ```with_std``` or ```with_epipline``` to ```False```."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support. ' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>');\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option);\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>');\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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
gitextract_bzvz72tp/ ├── .gitignore ├── CAPS/ │ ├── __init__.py │ ├── caps_model.py │ ├── criterion.py │ └── network.py ├── LICENSE ├── README.md ├── config.py ├── configs/ │ ├── extract_features_hpatches.yaml │ └── train_megadepth.yaml ├── dataloader/ │ ├── __init__.py │ ├── data_utils.py │ └── megadepth.py ├── environment.yml ├── extract_features.py ├── jupyter/ │ ├── functions.py │ └── visualization.ipynb ├── test/ │ └── eval_pose_megadepth.py ├── train.py └── utils.py
SYMBOL INDEX (96 symbols across 11 files)
FILE: CAPS/caps_model.py
class CAPSModel (line 13) | class CAPSModel():
method name (line 15) | def name(self):
method __init__ (line 18) | def __init__(self, args):
method set_input (line 34) | def set_input(self, data):
method forward (line 49) | def forward(self):
method backward_net (line 52) | def backward_net(self):
method optimize_parameters (line 57) | def optimize_parameters(self):
method test (line 64) | def test(self):
method extract_features (line 70) | def extract_features(self, im, coord):
method write_summary (line 76) | def write_summary(self, writer, n_iter):
method load_model (line 107) | def load_model(self, filename):
method load_from_ckpt (line 116) | def load_from_ckpt(self):
method save_model (line 146) | def save_model(self, step):
FILE: CAPS/criterion.py
class CtoFCriterion (line 5) | class CtoFCriterion(nn.Module):
method __init__ (line 6) | def __init__(self, args):
method homogenize (line 15) | def homogenize(self, coord):
method set_weight (line 19) | def set_weight(self, std, mask=None, regularizer=0.0):
method epipolar_cost (line 32) | def epipolar_cost(self, coord1, coord2, fmatrix):
method epipolar_loss (line 40) | def epipolar_loss(self, coord1, coord2, fmatrix, weight):
method cycle_consistency_loss (line 45) | def cycle_consistency_loss(self, coord1, coord1_loop, weight, th=40):
method forward (line 60) | def forward(self, coord1, data, fmatrix, pose, im_size):
FILE: CAPS/network.py
class CAPSNet (line 7) | class CAPSNet(nn.Module):
method __init__ (line 8) | def __init__(self, args, device):
method normalize (line 18) | def normalize(coord, h, w):
method denormalize (line 31) | def denormalize(coord_norm, h, w):
method ind2coord (line 43) | def ind2coord(self, ind, width):
method gen_grid (line 50) | def gen_grid(self, h_min, h_max, w_min, w_max, len_h, len_w):
method sample_feat_by_coord (line 55) | def sample_feat_by_coord(self, x, coord_n, norm=False):
method compute_prob (line 69) | def compute_prob(self, feat1, feat2):
method get_1nn_coord (line 87) | def get_1nn_coord(self, feat1, featmap2):
method get_expected_correspondence_locs (line 111) | def get_expected_correspondence_locs(self, feat1, featmap2, with_std=F...
method get_expected_correspondence_within_window (line 135) | def get_expected_correspondence_within_window(self, feat1, featmap2, c...
method forward (line 167) | def forward(self, im1, im2, coord1):
method extract_features (line 203) | def extract_features(self, im, coord):
method test (line 217) | def test(self, im1, im2, coord1):
function class_for_name (line 250) | def class_for_name(module_name, class_name):
class conv (line 256) | class conv(nn.Module):
method __init__ (line 257) | def __init__(self, num_in_layers, num_out_layers, kernel_size, stride):
method forward (line 267) | def forward(self, x):
class upconv (line 271) | class upconv(nn.Module):
method __init__ (line 272) | def __init__(self, num_in_layers, num_out_layers, kernel_size, scale):
method forward (line 277) | def forward(self, x):
class ResUNet (line 282) | class ResUNet(nn.Module):
method __init__ (line 283) | def __init__(self,
method skipconnect (line 320) | def skipconnect(self, x1, x2):
method forward (line 334) | def forward(self, x):
FILE: config.py
function get_args (line 4) | def get_args():
FILE: dataloader/data_utils.py
function skew (line 5) | def skew(x):
function rotateImage (line 11) | def rotateImage(image, angle):
function perspective_transform (line 28) | def perspective_transform(img, param=0.001):
function generate_query_kpts (line 45) | def generate_query_kpts(img, mode, num_pts, h, w):
function prune_kpts (line 78) | def prune_kpts(coord1, F_gt, im2_size, intrinsic1, intrinsic2, pose, d_m...
FILE: dataloader/megadepth.py
class MegaDepthLoader (line 17) | class MegaDepthLoader():
method __init__ (line 18) | def __init__(self, args):
method my_collate (line 24) | def my_collate(self, batch):
method load_data (line 29) | def load_data(self):
method name (line 32) | def name(self):
method __len__ (line 35) | def __len__(self):
class MegaDepth (line 39) | class MegaDepth(Dataset):
method __init__ (line 40) | def __init__(self, args):
method read_img_cam (line 67) | def read_img_cam(self):
method read_pairs (line 97) | def read_pairs(self):
method get_intrinsics (line 129) | def get_intrinsics(im_meta):
method get_extrinsics (line 135) | def get_extrinsics(im_meta):
method __getitem__ (line 143) | def __getitem__(self, item):
method __len__ (line 209) | def __len__(self):
FILE: extract_features.py
class HPatchDataset (line 13) | class HPatchDataset(Dataset):
method __init__ (line 14) | def __init__(self, imdir):
method __getitem__ (line 24) | def __getitem__(self, item):
method __len__ (line 37) | def __len__(self):
FILE: jupyter/functions.py
class Visualization (line 13) | class Visualization(object):
method __init__ (line 14) | def __init__(self, args):
method random_sample (line 20) | def random_sample(self):
method plot_img_pair (line 23) | def plot_img_pair(self, with_std=False, with_epipline=False):
method onclick (line 42) | def onclick(self, event):
method find_correspondence (line 53) | def find_correspondence(self):
method plot_correspondence (line 62) | def plot_correspondence(self):
FILE: test/eval_pose_megadepth.py
class MegaDepthPose (line 16) | class MegaDepthPose(object):
method __init__ (line 17) | def __init__(self, pose_args, mode):
method read_img_cam (line 27) | def read_img_cam(self):
method read_pairs (line 54) | def read_pairs(self):
method load_kp_desc (line 66) | def load_kp_desc(self, imf):
method compose_intrinsic_extrinsic (line 74) | def compose_intrinsic_extrinsic(self, imf):
method get_pose_error (line 86) | def get_pose_error(self, kp1, kp2, desc1, desc2, intrinsic1, intrinsic...
method visMatch (line 199) | def visMatch(self, imf1, imf2, pt1, pt2, mask, text, R_error, T_error,...
method pose_parallel (line 225) | def pose_parallel(self, imf1, imf2, vis=False):
method run (line 243) | def run(self):
FILE: train.py
function train_megadepth (line 9) | def train_megadepth(args):
FILE: utils.py
function cycle (line 6) | def cycle(iterable):
function evaluate_pose (line 12) | def evaluate_pose(E, P):
function average_precision (line 25) | def average_precision(labels, logits):
function homogenize (line 45) | def homogenize(kp):
function random_choice (line 55) | def random_choice(array, size):
function drawlines (line 65) | def drawlines(img1, img2, lines, pts1, pts2, color=None, thickness=-1):
function to_jet (line 87) | def to_jet(input, type='tensor', mode='HW1'):
function drawlinesMatch (line 110) | def drawlinesMatch(img1, img2, pts1, pts2, concat_row=True):
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,710K chars).
[
{
"path": ".gitignore",
"chars": 99,
"preview": "__pycache__/\nCAPS/__pycache__/\njupyter/.ipynb_checkpoints/\njupyter/__pycache__/\nlogs/\nout/\nmodels/\n"
},
{
"path": "CAPS/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "CAPS/caps_model.py",
"chars": 6501,
"preview": "import os\nimport cv2\nimport numpy as np\nimport torch\nfrom torch import optim\nfrom torch.autograd import Variable\nimport "
},
{
"path": "CAPS/criterion.py",
"chars": 4444,
"preview": "import torch\nimport torch.nn as nn\n\n\nclass CtoFCriterion(nn.Module):\n def __init__(self, args):\n super(CtoFCri"
},
{
"path": "CAPS/network.py",
"chars": 14525,
"preview": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport importlib\n\n\nclass CAPSNet(nn.Module):\n def "
},
{
"path": "LICENSE",
"chars": 1053,
"preview": "Copyright 2021 Qianqian Wang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this softw"
},
{
"path": "README.md",
"chars": 4250,
"preview": "# Learning Feature Descriptors using Camera Pose Supervision\n\nThis repository contains a PyTorch implementation of the p"
},
{
"path": "config.py",
"chars": 4916,
"preview": "import configargparse\n\n\ndef get_args():\n parser = configargparse.ArgParser(config_file_parser_class=configargparse.YA"
},
{
"path": "configs/extract_features_hpatches.yaml",
"chars": 398,
"preview": "# extract feature descriptors using pretrained model weights\n\nexpname: feature_extraction\n# replace the following with y"
},
{
"path": "configs/train_megadepth.yaml",
"chars": 185,
"preview": "# training from scratch using a single gpu, default configs in config.py file\nexp_name: train_caps\ndatadir: /phoenix/S7/"
},
{
"path": "dataloader/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "dataloader/data_utils.py",
"chars": 4836,
"preview": "import numpy as np\nimport cv2\n\n\ndef skew(x):\n return np.array([[0, -x[2], x[1]],\n [x[2], 0, -x[0]"
},
{
"path": "dataloader/megadepth.py",
"chars": 8492,
"preview": "import torch\nfrom torch.utils.data import Dataset\nimport os\nimport numpy as np\nimport cv2\nimport skimage.io as io\nimport"
},
{
"path": "environment.yml",
"chars": 1945,
"preview": "name: caps\nchannels:\n - pytorch\n - defaults\ndependencies:\n - _libgcc_mutex=0.1=main\n - blas=1.0=mkl\n - ca-certifica"
},
{
"path": "extract_features.py",
"chars": 2694,
"preview": "import torch\nfrom torch.utils.data import Dataset\nimport os\nimport numpy as np\nimport cv2\nimport skimage.io as io\nimport"
},
{
"path": "jupyter/functions.py",
"chars": 3606,
"preview": "import numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.lines as mlines\nimport torch\nimport sys\nsys.path.ap"
},
{
"path": "jupyter/visualization.ipynb",
"chars": 1625822,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"### Interactive demo\\n\",\n \"\\n\",\n"
},
{
"path": "test/eval_pose_megadepth.py",
"chars": 13842,
"preview": "import numpy as np\nimport cv2\nimport sys\nimport os\nsys.path.append('../')\nimport scipy\nimport utils\nfrom skimage import "
},
{
"path": "train.py",
"chars": 1389,
"preview": "import os\nimport config\nfrom tensorboardX import SummaryWriter\nfrom CAPS.caps_model import CAPSModel\nfrom dataloader.meg"
},
{
"path": "utils.py",
"chars": 4284,
"preview": "import numpy as np\nimport scipy\nimport cv2\n\n\ndef cycle(iterable):\n while True:\n for x in iterable:\n "
}
]
About this extraction
This page contains the full source code of the qianqianwang68/caps GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (1.6 MB), approximately 1.1M tokens, and a symbol index with 96 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.