• 如果不是要改进SuperPoint的模型的话,并没有必要重新训练。
  • SP在准确度上相比SIFT、SURF传统视觉方法有了提升,而且作者也提供了不错的预训练模型,如果仅需要提取特征点,或提取单应矩阵,可直接从demo的代码入手。
  • 花了一整晚拜读了match_features_demo.py的代码,在关键处都加了中文注释。便于将SuperPoint强大的特征提取能力快速应用于新的 idea~
  • 除了tensorflow,opencv和numpy再无其它依赖,并且tf也不需要gpu版,简单易用

(2020.4.17)
不过有一个问题,这个版本的superpoint强在可以用自己的数据训练,但是如果仅仅用于提取特征的话,还是应该用magicleap版。
对于一张240x320的图片,该版本在我的5500U上需要1.5s左右,而原版只需0.7s。速度差了一倍,而拼接质量相似
感觉sp论文里讲的实时性并没有那么实时,或者说只有在120x160这样的迷你图片才可以达到5fps左右的勉强实时,然而opencv里优化后的sift可以在0.05s内完成)

##Author: https://github.com/rpautrat/SuperPointimport argparse
from pathlib import Pathimport cv2
import numpy as np
import tensorflow as tf  # noqa: E402from settings import EXPER_PATH  # noqa: E402def extract_SIFT_keypoints_and_descriptors(img):gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)sift = cv2.xfeatures2d.SIFT_create()kp, desc = sift.detectAndCompute(np.squeeze(gray_img), None)return kp, descdef extract_superpoint_keypoints_and_descriptors(keypoint_map, descriptor_map,keep_k_points=1000):def select_k_best(points, k):""" Select the k most probable points (and strip their proba).points has shape (num_points, 3) where the last coordinate is the proba. """sorted_prob = points[points[:, 2].argsort(), :2]start = min(k, points.shape[0])return sorted_prob[-start:, :]# Extract keypointskeypoints = np.where(keypoint_map > 0)prob = keypoint_map[keypoints[0], keypoints[1]] #分别是所有的横坐标和所有的纵坐标,取出来的是所有概率值keypoints = np.stack([keypoints[0], keypoints[1], prob], axis=-1)#此时的keypoints是(n,3),n是疑似特征点的个数,3是横坐标,纵坐标,概率值keypoints = select_k_best(keypoints, keep_k_points)keypoints = keypoints.astype(int)# Get descriptors for keypointsdesc = descriptor_map[keypoints[:, 0], keypoints[:, 1]]#(n,256),n个关键点,256个描述符# Convert from just pts to cv2.KeyPointskeypoints = [cv2.KeyPoint(p[1], p[0], 1) for p in keypoints]return keypoints, descdef match_descriptors(kp1, desc1, kp2, desc2):# Match the keypoints with the warped_keypoints with nearest neighbor searchbf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=True)##是一个暴力匹配的对象,取desc1中一个描述子,再与desc2中的所有描述子计算欧式距离matches = bf.match(desc1, desc2)##上一行的返回值类似元组的集合(i,j)代表第一个集合的第i个点的最佳匹配是第二个集合的第j个点matches_idx = np.array([m.queryIdx for m in matches])  m_kp1 = [kp1[idx] for idx in matches_idx]matches_idx = np.array([m.trainIdx for m in matches])m_kp2 = [kp2[idx] for idx in matches_idx]####m_kp1是第一张图片的特征点,m_kp2是第二张图片的特征点,此时它们已经一一对应了(至少是最对应的,距离最小的return m_kp1, m_kp2, matches def compute_homography(matched_kp1, matched_kp2):##matched_kp1本来是KeyPoint对象,转化成了(n,2)的numpy数组,n是特征点个数,2是横、纵坐标matched_pts1 = cv2.KeyPoint_convert(matched_kp1)matched_pts2 = cv2.KeyPoint_convert(matched_kp2)# Estimate the homography between the matches using RANSACH, inliers = cv2.findHomography(matched_pts1[:, [1, 0]], ##通过两个图像的点集合,来寻找单应变换matched_pts2[:, [1, 0]],cv2.RANSAC)  ## H便是(3,3)的单应矩阵inliers = inliers.flatten()   #inliers 是正确的匹配return H, inliersdef preprocess_image(img_file, img_size):img = cv2.imread(img_file, cv2.IMREAD_COLOR)img = cv2.resize(img, img_size)img_orig = img.copy()img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)img = np.expand_dims(img, 2)  #(480,640,1)img = img.astype(np.float32)  # int --->> float ,以便下面归一化img_preprocessed = img / 255.# cv2.imshow("orig",img)# cv2.waitKey(0)return img_preprocessed, img_origif __name__ == '__main__':parser = argparse.ArgumentParser()parser = argparse.ArgumentParser(description='Compute the homography \between two images with the SuperPoint feature matches.')parser.add_argument('weights_name', type=str)parser.add_argument('img1_path', type=str)parser.add_argument('img2_path', type=str)parser.add_argument('--H', type=int, default=480,help='The height in pixels to resize the images to. \(default: 480)')parser.add_argument('--W', type=int, default=640,help='The width in pixels to resize the images to. \(default: 640)')parser.add_argument('--k_best', type=int, default=1000,help='Maximum number of keypoints to keep \(default: 1000)')args = parser.parse_args()weights_name = args.weights_nameimg1_file = args.img1_pathimg2_file = args.img2_pathimg_size = (args.W, args.H)keep_k_best = args.k_bestweights_root_dir = Path(EXPER_PATH, 'saved_models')weights_root_dir.mkdir(parents=True, exist_ok=True)weights_dir = Path(weights_root_dir, weights_name)graph = tf.Graph()with tf.Session(graph=graph) as sess:tf.saved_model.loader.load(sess,[tf.saved_model.tag_constants.SERVING],str(weights_dir))input_img_tensor = graph.get_tensor_by_name('superpoint/image:0')output_prob_nms_tensor = graph.get_tensor_by_name('superpoint/prob_nms:0')output_desc_tensors = graph.get_tensor_by_name('superpoint/descriptors:0')img1, img1_orig = preprocess_image(img1_file, img_size)out1 = sess.run([output_prob_nms_tensor, output_desc_tensors],feed_dict={input_img_tensor: np.expand_dims(img1, 0)})keypoint_map1 = np.squeeze(out1[0])  #将数组中维度为1的维度去掉 (1,480,640)---> (480,640)descriptor_map1 = np.squeeze(out1[1])kp1, desc1 = extract_superpoint_keypoints_and_descriptors(keypoint_map1, descriptor_map1, keep_k_best)#kp1是一个list,成员是KeyPoint类,desc1是(n个关键点,256)img2, img2_orig = preprocess_image(img2_file, img_size)out2 = sess.run([output_prob_nms_tensor, output_desc_tensors],feed_dict={input_img_tensor: np.expand_dims(img2, 0)})keypoint_map2 = np.squeeze(out2[0])descriptor_map2 = np.squeeze(out2[1])kp2, desc2 = extract_superpoint_keypoints_and_descriptors(keypoint_map2, descriptor_map2, keep_k_best)############此时两张图片的特征点和描述符已分别保存于kp1,desc1,kp2,desc2############## Match and get rid of outliersm_kp1, m_kp2, matches = match_descriptors(kp1, desc1, kp2, desc2)##m_kp1是第一张图片的特征点,m_kp2是第二张图片的特征点,此时它们已经一一对应了(至少是最对应的,距离最小的H, inliers = compute_homography(m_kp1, m_kp2)###############  H是单应变换的3x3矩阵 ,inliers 是成功的匹配,可以理解为在每个匹配上的一个bool值的list# Draw SuperPoint matchesmatches = np.array(matches)[inliers.astype(bool)].tolist()##matches原来是所有的所有特征点之间的最匹配(欧氏距离最短),现在使用inliers过滤,只剩下了真正成功的匹配(可以理解为加了阈值matched_img = cv2.drawMatches(img1_orig, kp1, img2_orig, kp2, matches,None, matchColor=(0, 255, 0),##匹配用绿色 (BGR)singlePointColor=(0, 0, 255))##未匹配用红色cv2.imshow("SuperPoint matches", matched_img)# Compare SIFT matchessift_kp1, sift_desc1 = extract_SIFT_keypoints_and_descriptors(img1_orig)sift_kp2, sift_desc2 = extract_SIFT_keypoints_and_descriptors(img2_orig)sift_m_kp1, sift_m_kp2, sift_matches = match_descriptors(sift_kp1, sift_desc1, sift_kp2, sift_desc2)sift_H, sift_inliers = compute_homography(sift_m_kp1, sift_m_kp2)# Draw SIFT matchessift_matches = np.array(sift_matches)[sift_inliers.astype(bool)].tolist()sift_matched_img = cv2.drawMatches(img1_orig, sift_kp1, img2_orig,sift_kp2, sift_matches, None,matchColor=(0, 255, 0),singlePointColor=(0, 0, 255))cv2.imshow("SIFT matches", sift_matched_img)cv2.waitKey(0)

SuperPoint学习---demo代码理解相关推荐

  1. DLT(DeepLearningTracker)学习与代码理解 (1)

    持续更新中-- DLT是香港科大的Naiyan Wang和Dit-Yan Yeung提出的,文章和代码见 http://winsty.net/dlt.html 相关论文是 Learning A Dee ...

  2. JAVA泛型学习demo代码

    利用JAVA的泛型实现了简单的队列. 1.主测试类 hello.java package gensoku;public class hello {public static void main(Str ...

  3. X3D代码理解之test(cfg)

    目录 test(cfg) construct_loader(cfg, split, is_precise_bn=False) Kinetics.__init__(self, cfg, mode, nu ...

  4. 【HSI】高光谱的数据集分类深度学习实战及代码理解

    [HSI]高光谱的数据集分类深度学习实战及代码理解 文章目录 [HSI]高光谱的数据集分类深度学习实战及代码理解 一.配置文件编写 二.高光谱图像的处理 2.1图像数据变换 2.2 数据整合 2.3 ...

  5. MTCNN算法与代码理解—人脸检测和人脸对齐联合学习

    MTCNN算法与代码理解-人脸检测和人脸对齐联合学习 写在前面 主页:https://kpzhang93.github.io/MTCNN_face_detection_alignment/index. ...

  6. 【Pytorch深度学习实践】B站up刘二大人之BasicCNN Advanced CNN -代码理解与实现(9/9)

    这是刘二大人系列课程笔记的 最后一个笔记了,介绍的是 BasicCNN 和 AdvancedCNN ,我做图像,所以后面的RNN我可能暂时不会花时间去了解了: 写在前面: 本节把基础个高级CNN放在一 ...

  7. 如何阅读一份深度学习项目代码?

    犹豫很久要不要把读代码这个事情专门挑出来写成一篇推文.毕竟读代码嘛,大家可能都会读.而且笔者个人读的和写的代码量也并不足以到指导大家读代码的程度.但笔者还是决定大胆地写一点:就当是给自己设立今后读代码 ...

  8. 深度学习项目代码阅读建议

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|机器学习实验室 犹豫很久要不要把读代码这个事情专门挑出来写 ...

  9. WP7开发—Silverlight多点触摸事件详解【含Demo代码】

    最近在学习WP7的Silverlight编程,就把学习到知识点整理为日志,方便自己理解深刻点,也作为笔记和备忘,如有不合理或者错误之处,还恳请指正. WP7的支持多点触摸,有两种不同的编程模式: 1. ...

  10. 如何高效的学习TensorFlow代码?

    如何高效的学习TensorFlow代码? 如题,或者如何掌握TensorFlow,应用到任何领域? 添加评论分享 10 个回答 爱琳李,老李,明天就辍学了 8 人赞同 本来都忘了这个问题了,不过看到很 ...

最新文章

  1. jar中的类文件更新遇到的问题:请分析下原因呢
  2. CSS中z-index全解析
  3. 项目开发中发布更新文档备注
  4. jq之animate()队列
  5. micro 和 macro F1 值 的区别?
  6. md5 java_JAVA使用MD5加密解密
  7. CDA Level1 考试心得
  8. 第 2 届河北省大学生程序设计竞赛(河北省赛)-Problem C. icebound 的账单-题解
  9. HDU 4699 题解
  10. 有赞订单导出的配置化实践
  11. Python中的逻辑运算符:‘and‘与‘or‘的用法
  12. 产业分析:二次元产业研究报告
  13. espn配置路由_华为敏捷网络解决方案.ppt
  14. Java 中continue基础用法
  15. 【Python】爬取百度图片和必应图片
  16. 【蛮力算法】数据结构与算法
  17. c语言五子棋游戏心得体会,下五子棋执白子之心得
  18. 系统辨识理论及MATLAB仿真——学习笔记(1)
  19. 推荐几个常用在线图工具(支持时序图、用例图、类图、活动图、组件图、状态图、对象图、部署图等。同时还支持非 UML 图的甘特图、架构图等)
  20. 虚拟web主机搭建 ---基于域名、基于IP、基于端口

热门文章

  1. ASP.NET2.0(学习第一天)
  2. HCIE-Security Day2:防火墙安全区域、安全级别的理解
  3. web安全day42:使用BurpSuite理解Web工作机制
  4. NP、OSPF监测调试
  5. LAMP架构调优(五)——网页缓存设置
  6. BGP联邦原理及配置实例
  7. C++ 从入门到入土(English Version)Section 9 : Computer Graphics and Command Prompt
  8. js学习(六)- js对象创建
  9. ES6 = 函数参数
  10. 转: hibernate配置文件hibernate.cfg.xml和.hbm.xml的详细解释