├── README.md ├── SLAM ├── Zdzn.yaml ├── launch │ └── SP.launch └── src │ ├── .ros_sp.cc.swp │ └── ros_sp.cc ├── SimEnv └── README.md ├── SuperPointPretrainedNetwork ├── README.md ├── assets │ ├── DL4VSLAM_talk.pdf │ ├── icl_snippet │ │ ├── 250.png │ │ ├── 254.png │ │ ├── 258.png │ │ ├── 262.png │ │ ├── 266.png │ │ ├── 270.png │ │ ├── 274.png │ │ ├── 278.png │ │ ├── 282.png │ │ ├── 286.png │ │ ├── 290.png │ │ ├── 294.png │ │ ├── 298.png │ │ ├── 302.png │ │ ├── 306.png │ │ ├── 310.png │ │ ├── 314.png │ │ ├── 318.png │ │ ├── 322.png │ │ ├── 326.png │ │ ├── 330.png │ │ ├── 334.png │ │ ├── 338.png │ │ ├── 342.png │ │ ├── 346.png │ │ ├── 350.png │ │ ├── 354.png │ │ ├── 358.png │ │ ├── 362.png │ │ ├── 366.png │ │ └── magicleap.png │ ├── magicleap.png │ ├── nyu_snippet.mp4 │ ├── processed_freiburg.gif │ ├── processed_icl.gif │ ├── processed_kitti.gif │ ├── processed_monovo.gif │ ├── processed_ms7.gif │ └── processed_nyu.gif ├── demo_superpoint.py ├── superpoint_v1.pth └── test.png └── modules.png /README.md: -------------------------------------------------------------------------------- 1 | # SuperPoint_SLAM 2 | 使用SuperPoint方法提取特征点,来代替ORB-SLAM中的ORB特征。整个项目会由三个部分,分别是: 3 | 4 | - 仿真环境**SimEnv**。主要实现仿真环境的构建、机器人及其传感器的放置、机器人与环境的交互,具有键盘控制机器人运动的功能,需要发布`/camera/rgb/`和`/camera/depth/`的相关消息。 5 | - **SLAM模块**。基于ORB-SLAM2框架,将ORB特征全面替换为SuperPoint特征,其中为了实现建图功能,额外添加了`PointCloud.cc`,用于生成点云,然后建图。 6 | - SuperPoint特征提取**SuperPointPretrainedNetwork**。运行其中的`demo_superpoint.py`,能够实时处理RGB图片,生成特征点文件。 7 | 8 | ### 模块间关系 9 | 10 | ![](./modules.png) 11 | 12 | 为了保证时间能够对上,图中的四步能够保持一致(在特征点处理图片时,不会再有新的图片保存下来),我们设计了如下方法: 13 | 14 | 基本思想:通过往`/dev/shm/tmpSP/`中读写文件实现同步,`/dev/shm/`这个文件夹下的文件能够实现快速读写。 15 | 16 | ```c++ 17 | cv::imwrite(filename, RGB); 18 | system("touch /dev/shm/tmpSP/ready.txt"); 19 | while( ! file_exist("/dev/shm/tmpSP/done.txt")){ 20 | usleep(5000); 21 | } 22 | Camera_Pose = mpSLAM->TrackRGBD(cv_ptrRGB->image,cv_ptrD->image,cv_ptrRGB->header.stamp.toSec()); 23 | system("rm -r /dev/shm/tmpSP/*"); 24 | ``` 25 | 26 | SLAM模块中先保存接收到的图片,然后创建一个`ready.txt`,用来标记图片已保存,然后会一直查找文件夹下有无`done.txt`,若查找到,才可继续运行并接收下一张图片,在接收图片前要清空文件夹。 27 | 28 | ```python 29 | while ( not os.path.exists(listenlingfolder+'ready.txt') ): 30 | time.sleep(0.01) 31 | # print("1234") 32 | os.remove(listenlingfolder+'ready.txt') 33 | f_list = glob.glob( listenlingfolder+'/*.png' ) 34 | assert len(f_list) == 1 35 | for file_i in f_list: 36 | if file_i.split('.')[-1] == 'png': 37 | print(file_i) 38 | targetpre = re.sub(r'\.png', "", file_i) 39 | print(targetpre) 40 | getImage2SP(file_i,targetpre,fe) 41 | os.remove(file_i) 42 | pathlib.Path(listenlingfolder + '/done.txt').touch() 43 | while ( os.path.exists(listenlingfolder+'/done.txt') ): 44 | time.sleep(0.01) 45 | ``` 46 | 47 | 特征点模块中会一直查询有无`ready.txt`文件,查找到了,说明图片已存在,就可开始处理。处理前先删除`ready.txt`,除了完后会生成`kpts.txt`和`feats.txt`,同时再新建一个`done.txt`,用来告诉SLAM模块上一帧已经处理完毕,可以接收新的图片了。 48 | 49 | ### 主要问题 50 | 51 | - **特征点数不够**,导致效果不佳。主要原因可能还是仿真环境不够逼真,仿真环境中的RGBD相机获取到的图片与实际场景的照片差距较大。解决方案:使用AirSim替换Gazebo、改变原先使用的相机。 52 | - **各模块间的帧率不匹配**,造成仿真时较卡顿。解决方案:仿真时具体调整。 53 | - 优化处理速度,尽可能提高帧率。 -------------------------------------------------------------------------------- /SLAM/Zdzn.yaml: -------------------------------------------------------------------------------- 1 | %YAML:1.0 2 | 3 | #-------------------------------------------------------------------------------------------- 4 | # Camera Parameters. Adjust them! 5 | #-------------------------------------------------------------------------------------------- 6 | 7 | # Camera calibration and distortion parameters (OpenCV) 8 | Camera.fx: 277.19135641132203 9 | Camera.fy: 277.19135641132203 10 | Camera.cx: 160.5 11 | Camera.cy: 120.5 12 | 13 | Camera.k1: 0.0 14 | Camera.k2: 0.0 15 | Camera.p1: 0.0 16 | Camera.p2: 0.0 17 | 18 | Camera.width: 320 19 | Camera.height: 240 20 | 21 | # Camera frames per second 22 | Camera.fps: 50.0 23 | 24 | # IR projector baseline times fx (aprox.) 25 | Camera.bf: 40.0 26 | 27 | # Color order of the images (0: BGR, 1: RGB. It is ignored if images are grayscale) 28 | Camera.RGB: 1 29 | 30 | # Close/Far threshold. Baseline times. 31 | ThDepth: 40.0 32 | 33 | # Deptmap values factor 34 | DepthMapFactor: 1.0 35 | 36 | #-------------------------------------------------------------------------------------------- 37 | # ORB Parameters 38 | #-------------------------------------------------------------------------------------------- 39 | 40 | # ORB Extractor: Number of features per image 41 | ORBextractor.nFeatures: 1000 42 | 43 | # ORB Extractor: Scale factor between levels in the scale pyramid 44 | ORBextractor.scaleFactor: 1.2 45 | 46 | # ORB Extractor: Number of levels in the scale pyramid 47 | ORBextractor.nLevels: 8 48 | 49 | # ORB Extractor: Fast threshold 50 | # Image is divided in a grid. At each cell FAST are extracted imposing a minimum response. 51 | # Firstly we impose iniThFAST. If no corners are detected we impose a lower value minThFAST 52 | # You can lower these values if your images have low contrast 53 | ORBextractor.iniThFAST: 20 54 | ORBextractor.minThFAST: 7 55 | 56 | #-------------------------------------------------------------------------------------------- 57 | # Viewer Parameters 58 | #-------------------------------------------------------------------------------------------- 59 | Viewer.KeyFrameSize: 0.05 60 | Viewer.KeyFrameLineWidth: 1 61 | Viewer.GraphLineWidth: 0.9 62 | Viewer.PointSize:2 63 | Viewer.CameraSize: 0.08 64 | Viewer.CameraLineWidth: 3 65 | Viewer.ViewpointX: 0 66 | Viewer.ViewpointY: -0.7 67 | Viewer.ViewpointZ: -1.8 68 | Viewer.ViewpointF: 500 69 | PointCloudMapping.Resolution: 0.05 70 | -------------------------------------------------------------------------------- /SLAM/launch/SP.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SLAM/src/.ros_sp.cc.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SLAM/src/.ros_sp.cc.swp -------------------------------------------------------------------------------- /SLAM/src/ros_sp.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is part of ORB-SLAM2. 3 | * 4 | * Copyright (C) 2014-2016 Raúl Mur-Artal (University of Zaragoza) 5 | * For more information see 6 | * 7 | * ORB-SLAM2 is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * ORB-SLAM2 is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ORB-SLAM2. If not, see . 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include "geometry_msgs/PoseWithCovarianceStamped.h" 43 | 44 | #include 45 | 46 | #include"../../../include/System.h" 47 | 48 | cv::Mat RGB; 49 | cv::Mat DEPTH; 50 | ros::Time rgbTime; 51 | bool newImgFlg=false; 52 | using namespace std; 53 | 54 | ros::Publisher CamPose_Pub; 55 | ros::Publisher Camodom_Pub; 56 | ros::Publisher odom_pub; 57 | 58 | geometry_msgs::PoseStamped Cam_Pose; 59 | geometry_msgs::PoseWithCovarianceStamped Cam_odom; 60 | 61 | cv::Mat Camera_Pose; 62 | tf::Transform orb_slam; 63 | tf::TransformBroadcaster * orb_slam_broadcaster; 64 | std::vector Pose_quat(4); 65 | std::vector Pose_trans(3); 66 | 67 | ros::Time current_time, last_time; 68 | double lastx=0,lasty=0,lastth=0; 69 | 70 | void Pub_CamPose(cv::Mat &pose); 71 | 72 | class ImageGrabber 73 | { 74 | public: 75 | ImageGrabber(ORB_SLAM2::System* pSLAM):mpSLAM(pSLAM){} 76 | 77 | void GrabRGBD(const sensor_msgs::ImageConstPtr& msgRGB,const sensor_msgs::ImageConstPtr& msgD); 78 | 79 | ORB_SLAM2::System* mpSLAM; 80 | }; 81 | 82 | int main(int argc, char **argv) 83 | { 84 | ros::init(argc, argv, "SP"); 85 | ros::start(); 86 | 87 | if(argc != 3) 88 | { 89 | cerr << endl << "Usage: rosrun ORB_SLAM2 RGBD path_to_vocabulary path_to_settings" << endl; 90 | ros::shutdown(); 91 | return 1; 92 | } 93 | 94 | // Create SLAM system. It initializes all system threads and gets ready to process frames. 95 | ORB_SLAM2::System SLAM(argv[1],argv[2],ORB_SLAM2::System::RGBD, true, false, false, false, false); //viewer, localmap, loop, bow, trackonly 96 | 97 | ImageGrabber igb(&SLAM); 98 | 99 | ros::NodeHandle nh; 100 | message_filters::Subscriber rgb_sub(nh, "/camera/rgb/image_raw", 1); 101 | message_filters::Subscriber depth_sub(nh, "/camera/depth_registered/image_raw", 1); 102 | typedef message_filters::sync_policies::ApproximateTime sync_pol; 103 | message_filters::Synchronizer sync(sync_pol(10), rgb_sub,depth_sub); 104 | sync.registerCallback(boost::bind(&ImageGrabber::GrabRGBD,&igb,_1,_2)); 105 | 106 | CamPose_Pub = nh.advertise("/Camera_Pose",1); 107 | Camodom_Pub = nh.advertise("/Camera_Odom", 1); 108 | odom_pub = nh.advertise("/odom", 50); 109 | 110 | while( ros::ok() ) 111 | { 112 | ros::spinOnce(); 113 | if(!newImgFlg) 114 | { 115 | continue; 116 | } 117 | Pub_CamPose(Camera_Pose); 118 | newImgFlg=false; 119 | } 120 | // Stop all threads 121 | SLAM.Shutdown(); 122 | 123 | // Save camera trajectory 124 | SLAM.SaveKeyFrameTrajectoryTUM("/home/tsui/KeyFrameTrajectory.txt"); 125 | 126 | ros::shutdown(); 127 | 128 | return 0; 129 | } 130 | 131 | inline bool file_exist (const std::string& name) { 132 | ifstream f(name.c_str()); 133 | return f.good(); 134 | } 135 | 136 | int GetFiles(char** file_list, const char* dirname) 137 | { 138 | DIR * dir; 139 | struct dirent * ptr; 140 | int i=0; 141 | dir = opendir(dirname); //打开一个目录 142 | while((ptr = readdir(dir)) != NULL) //循环读取目录数据 143 | { 144 | //printf("d_name : %s\n", ptr->d_name); //输出文件名 145 | strcpy(file_list[i],ptr->d_name ); //存储到数组 146 | if ( ++i>=5 ) break; 147 | } 148 | closedir(dir);//关闭目录指针 149 | return (i - 2); 150 | } 151 | 152 | void ImageGrabber::GrabRGBD(const sensor_msgs::ImageConstPtr& msgRGB,const sensor_msgs::ImageConstPtr& msgD) 153 | { 154 | // Copy the ros image message to cv::Mat. 155 | newImgFlg=true; 156 | cv_bridge::CvImageConstPtr cv_ptrRGB; 157 | try 158 | { 159 | cv_ptrRGB = cv_bridge::toCvShare(msgRGB); 160 | } 161 | catch (cv_bridge::Exception& e) 162 | { 163 | ROS_ERROR("cv_bridge exception: %s", e.what()); 164 | return; 165 | } 166 | 167 | cv_bridge::CvImageConstPtr cv_ptrD; 168 | try 169 | { 170 | cv_ptrD = cv_bridge::toCvShare(msgD); 171 | } 172 | catch (cv_bridge::Exception& e) 173 | { 174 | ROS_ERROR("cv_bridge exception: %s", e.what()); 175 | return; 176 | } 177 | 178 | // Get RGB and Depth image 179 | RGB=cv_ptrRGB->image; 180 | DEPTH=cv_ptrD->image; 181 | rgbTime=msgRGB->header.stamp; 182 | 183 | string dir = "/dev/shm/tmpSP/"; //文件夹 184 | string filename = dir + to_string(rgbTime.toSec()) + ".png";; //RGB图文件名 185 | // 读取特征点文件信息 186 | 187 | cv::imwrite(filename, RGB); 188 | system("touch /dev/shm/tmpSP/ready.txt"); 189 | while( ! file_exist("/dev/shm/tmpSP/done.txt")){ 190 | usleep(5000); 191 | } 192 | Camera_Pose = mpSLAM->TrackRGBD(cv_ptrRGB->image,cv_ptrD->image,cv_ptrRGB->header.stamp.toSec()); 193 | system("rm -r /dev/shm/tmpSP/*"); 194 | } 195 | 196 | void Pub_CamPose(cv::Mat &pose) 197 | { 198 | cv::Mat Rwc(3,3,CV_32F); 199 | cv::Mat twc(3,1,CV_32F); 200 | Eigen::Matrix rotationMat; 201 | orb_slam_broadcaster = new tf::TransformBroadcaster; 202 | if(pose.dims<2 || pose.rows < 3) 203 | { 204 | Rwc = Rwc; 205 | twc = twc; 206 | } 207 | else 208 | { 209 | Rwc = pose.rowRange(0,3).colRange(0,3).t(); 210 | twc = -Rwc*pose.rowRange(0,3).col(3); 211 | 212 | rotationMat << Rwc.at(0,0), Rwc.at(0,1), Rwc.at(0,2), 213 | Rwc.at(1,0), Rwc.at(1,1), Rwc.at(1,2), 214 | Rwc.at(2,0), Rwc.at(2,1), Rwc.at(2,2); 215 | Eigen::Quaterniond Q(rotationMat); 216 | 217 | Pose_quat[0] = Q.x(); Pose_quat[1] = Q.y(); 218 | Pose_quat[2] = Q.z(); Pose_quat[3] = Q.w(); 219 | 220 | Pose_trans[0] = twc.at(0); 221 | Pose_trans[1] = twc.at(1); 222 | Pose_trans[2] = twc.at(2); 223 | 224 | orb_slam.setOrigin(tf::Vector3(Pose_trans[2], -Pose_trans[0], -Pose_trans[1])); 225 | orb_slam.setRotation(tf::Quaternion(Q.z(), -Q.x(), -Q.y(), Q.w())); 226 | orb_slam_broadcaster->sendTransform(tf::StampedTransform(orb_slam, ros::Time::now(), "/map", "/base_link")); 227 | 228 | Cam_Pose.header.stamp = ros::Time::now(); 229 | Cam_Pose.header.frame_id = "/map"; 230 | tf::pointTFToMsg(orb_slam.getOrigin(), Cam_Pose.pose.position); 231 | tf::quaternionTFToMsg(orb_slam.getRotation(), Cam_Pose.pose.orientation); 232 | 233 | Cam_odom.header.stamp = ros::Time::now(); 234 | Cam_odom.header.frame_id = "/map"; 235 | tf::pointTFToMsg(orb_slam.getOrigin(), Cam_odom.pose.pose.position); 236 | tf::quaternionTFToMsg(orb_slam.getRotation(), Cam_odom.pose.pose.orientation); 237 | Cam_odom.pose.covariance = {0.01, 0, 0, 0, 0, 0, 238 | 0, 0.01, 0, 0, 0, 0, 239 | 0, 0, 0.01, 0, 0, 0, 240 | 0, 0, 0, 0.01, 0, 0, 241 | 0, 0, 0, 0, 0.01, 0, 242 | 0, 0, 0, 0, 0, 0.01}; 243 | 244 | CamPose_Pub.publish(Cam_Pose); 245 | Camodom_Pub.publish(Cam_odom); 246 | 247 | nav_msgs::Odometry odom; 248 | odom.header.stamp =ros::Time::now(); 249 | odom.header.frame_id = "/map"; 250 | 251 | // Set the position 252 | odom.pose.pose.position = Cam_odom.pose.pose.position; 253 | odom.pose.pose.orientation = Cam_odom.pose.pose.orientation; 254 | 255 | // Set the velocity 256 | odom.child_frame_id = "/base_link"; 257 | current_time = ros::Time::now(); 258 | double dt = (current_time - last_time).toSec(); 259 | double vx = (Cam_odom.pose.pose.position.x - lastx)/dt; 260 | double vy = (Cam_odom.pose.pose.position.y - lasty)/dt; 261 | double vth = (Cam_odom.pose.pose.orientation.z - lastth)/dt; 262 | 263 | odom.twist.twist.linear.x = vx; 264 | odom.twist.twist.linear.y = vy; 265 | odom.twist.twist.angular.z = vth; 266 | 267 | // Publish the message 268 | odom_pub.publish(odom); 269 | 270 | last_time = current_time; 271 | lastx = Cam_odom.pose.pose.position.x; 272 | lasty = Cam_odom.pose.pose.position.y; 273 | lastth = Cam_odom.pose.pose.orientation.z; 274 | } 275 | } 276 | 277 | 278 | -------------------------------------------------------------------------------- /SimEnv/README.md: -------------------------------------------------------------------------------- 1 | # 仿真环境 2 | 3 | 待补全 -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Research @ Magic Leap 4 | 5 | # SuperPoint Weights File and Demo Script 6 | 7 | ## Introduction 8 | This repo contains the pretrained SuperPoint network, as implemented by the originating authors. SuperPoint is a research project at Magic Leap. The SuperPoint network is a fully convolutional deep neural network trained to detect interest points and compute their accompanying descriptors. The detected points and descriptors can thus be used for various image-to-image matching tasks. For more details please see 9 | 10 | * Full paper PDF: [SuperPoint: Self-Supervised Interest Point Detection and Description](https://arxiv.org/abs/1712.07629) 11 | 12 | * Presentation PDF: [Talk at CVPR Deep Learning for Visual SLAM Workshop 2018](assets/DL4VSLAM_talk.pdf) 13 | 14 | * Authors: *Daniel DeTone, Tomasz Malisiewicz, Andrew Rabinovich* 15 | 16 | This demo showcases a simple sparse optical flow point tracker that uses SuperPoint to detect points and match them across video sequences. The repo contains two core files (1) a PyTorch weights file and (2) a python deployment script that defines the network, loads images and runs the pytorch weights file on them, creating a sparse optical flow visualization. Here are videos of the demo running on various publically available datsets: 17 | 18 | Freiburg RGBD: 19 | 20 | 21 | KITTI: 22 | 23 | 24 | Microsoft 7 Scenes: 25 | 26 | 27 | MonoVO: 28 | 29 | 30 | 31 | ## Dependencies 32 | * [OpenCV](https://opencv.org/) python >= 3.4 33 | * [PyTorch](https://pytorch.org/) >= 0.4 34 | 35 | This repo depends on a few standard pythonic modules, plus OpenCV and PyTorch. These commands usually work (tested on Mac and Ubuntu) for installing the two libraries: 36 | 37 | ```sh 38 | pip install opencv-python 39 | pip install torch 40 | ``` 41 | 42 | ## Running the Demo 43 | This demo will run the SuperPoint network on an image sequence and compute points and descriptors from the images, using a helper class called `SuperPointFrontend`. The tracks are formed by the `PointTracker` class which finds sequential pair-wise nearest neighbors using two-way matching of the points' descriptors. The demo script uses a helper class called `VideoStreamer` which can process inputs from three different input streams: 44 | 45 | 1. A directory of images, such as .png or .jpg 46 | 2. A video file, such as .mp4 or .avi 47 | 3. A USB Webcam 48 | 49 | ### Run the demo on provided directory of images in CPU-mode: 50 | 51 | ```sh 52 | ./demo_superpoint.py assets/icl_snippet/ 53 | ``` 54 | You should see the following output from the ICL-NUIM sequence snippet: 55 | 56 | 57 | 58 | ### Run the demo on provided .mp4 file in GPU-mode: 59 | 60 | ```sh 61 | ./demo_superpoint.py assets/nyu_snippet.mp4 --cuda 62 | ``` 63 | You should see the following output from the NYU sequence snippet: 64 | 65 | 66 | 67 | ### Run a live demo via webcam (id #1) in CPU-mode: 68 | 69 | ```sh 70 | ./demo_superpoint.py camera --camid=1 71 | ``` 72 | 73 | ### Run the demo on a remote GPU (no display) on 640x480 images and write the output to `myoutput/` 74 | ```sh 75 | ./demo_superpoint.py assets/icl_snippet/ --W=640 --H=480 --no_display --write --write_dir=myoutput/ 76 | ``` 77 | 78 | ### Additional useful command line parameters 79 | 80 | * Use `--H` to change the input image height (default: 120). 81 | * Use `--W` to change the input image width (default: 160). 82 | * Use `--display_scale` to scale the output visualization image height and width (default: 2). 83 | * Use `--cuda` flag to enable the GPU. 84 | * Use `--img_glob` to change the image file extension (default: *.png). 85 | * Use `--min_length` to change the minimum track length (default: 2). 86 | * Use `--max_length` to change the maximum track length (default: 5). 87 | * Use `--conf_thresh` to change the point confidence threshold (default: 0.015). 88 | * Use `--nn_thresh` to change the descriptor matching distance threshold (default: 0.7). 89 | * Use `--show_extra` to show more computer vision outputs. 90 | * Press the `q` key to quit. 91 | 92 | 93 | ## BibTeX Citation 94 | ```txt 95 | @inproceedings{detone18superpoint, 96 | author = {Daniel DeTone and 97 | Tomasz Malisiewicz and 98 | Andrew Rabinovich}, 99 | title = {SuperPoint: Self-Supervised Interest Point Detection and Description}, 100 | booktitle = {CVPR Deep Learning for Visual SLAM Workshop}, 101 | year = {2018}, 102 | url = {http://arxiv.org/abs/1712.07629} 103 | } 104 | ``` 105 | 106 | ## Additional Notes 107 | * We do not intend to release the SuperPoint training or evaluation code, please do not email us to ask for it. 108 | * We do not intend to release the Synthetic Shapes dataset used to bootstrap the SuperPoint training, please do not email us to ask for it. 109 | * We use bi-linear interpolation rather than the bi-cubic interpolation described in the paper to sample the descriptor as it is faster and gave us similar results. 110 | -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/DL4VSLAM_talk.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/DL4VSLAM_talk.pdf -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/250.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/254.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/254.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/258.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/258.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/262.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/262.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/266.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/266.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/270.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/274.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/274.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/278.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/278.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/282.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/282.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/286.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/286.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/290.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/290.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/294.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/294.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/298.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/298.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/302.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/306.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/310.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/314.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/314.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/318.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/318.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/322.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/322.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/326.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/326.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/330.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/330.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/334.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/334.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/338.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/338.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/342.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/342.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/346.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/346.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/350.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/350.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/354.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/354.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/358.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/358.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/362.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/362.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/366.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/366.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/icl_snippet/magicleap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/icl_snippet/magicleap.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/magicleap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/magicleap.png -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/nyu_snippet.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/nyu_snippet.mp4 -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/processed_freiburg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/processed_freiburg.gif -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/processed_icl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/processed_icl.gif -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/processed_kitti.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/processed_kitti.gif -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/processed_monovo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/processed_monovo.gif -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/processed_ms7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/processed_ms7.gif -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/assets/processed_nyu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/assets/processed_nyu.gif -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/demo_superpoint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # %BANNER_BEGIN% 4 | # --------------------------------------------------------------------- 5 | # %COPYRIGHT_BEGIN% 6 | # 7 | # Magic Leap, Inc. ("COMPANY") CONFIDENTIAL 8 | # 9 | # Unpublished Copyright (c) 2018 10 | # Magic Leap, Inc., All Rights Reserved. 11 | # 12 | # NOTICE: All information contained herein is, and remains the property 13 | # of COMPANY. The intellectual and technical concepts contained herein 14 | # are proprietary to COMPANY and may be covered by U.S. and Foreign 15 | # Patents, patents in process, and are protected by trade secret or 16 | # copyright law. Dissemination of this information or reproduction of 17 | # this material is strictly forbidden unless prior written permission is 18 | # obtained from COMPANY. Access to the source code contained herein is 19 | # hereby forbidden to anyone except current COMPANY employees, managers 20 | # or contractors who have executed Confidentiality and Non-disclosure 21 | # agreements explicitly covering such access. 22 | # 23 | # The copyright notice above does not evidence any actual or intended 24 | # publication or disclosure of this source code, which includes 25 | # information that is confidential and/or proprietary, and is a trade 26 | # secret, of COMPANY. ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, 27 | # PUBLIC PERFORMANCE, OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS 28 | # SOURCE CODE WITHOUT THE EXPRESS WRITTEN CONSENT OF COMPANY IS 29 | # STRICTLY PROHIBITED, AND IN VIOLATION OF APPLICABLE LAWS AND 30 | # INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE 31 | # CODE AND/OR RELATED INFORMATION DOES NOT CONVEY OR IMPLY ANY RIGHTS 32 | # TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, OR TO MANUFACTURE, 33 | # USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. 34 | # 35 | # %COPYRIGHT_END% 36 | # ---------------------------------------------------------------------- 37 | # %AUTHORS_BEGIN% 38 | # 39 | # Originating Authors: Daniel DeTone (ddetone) 40 | # Tomasz Malisiewicz (tmalisiewicz) 41 | # 42 | # %AUTHORS_END% 43 | # --------------------------------------------------------------------*/ 44 | # %BANNER_END% 45 | 46 | 47 | import argparse 48 | import glob 49 | import numpy as np 50 | import os 51 | import time 52 | import re 53 | 54 | 55 | import cv2 56 | import torch 57 | import pathlib 58 | 59 | # Stub to warn about opencv version. 60 | if int(cv2.__version__[0]) < 3: # pragma: no cover 61 | print('Warning: OpenCV 3 is not installed') 62 | 63 | # Jet colormap for visualization. 64 | myjet = np.array([[0. , 0. , 0.5 ], 65 | [0. , 0. , 0.99910873], 66 | [0. , 0.37843137, 1. ], 67 | [0. , 0.83333333, 1. ], 68 | [0.30044276, 1. , 0.66729918], 69 | [0.66729918, 1. , 0.30044276], 70 | [1. , 0.90123457, 0. ], 71 | [1. , 0.48002905, 0. ], 72 | [0.99910873, 0.07334786, 0. ], 73 | [0.5 , 0. , 0. ]]) 74 | 75 | class SuperPointNet(torch.nn.Module): 76 | """ Pytorch definition of SuperPoint Network. """ 77 | def __init__(self): 78 | super(SuperPointNet, self).__init__() 79 | self.relu = torch.nn.ReLU(inplace=True) 80 | self.pool = torch.nn.MaxPool2d(kernel_size=2, stride=2) 81 | c1, c2, c3, c4, c5, d1 = 64, 64, 128, 128, 256, 256 82 | # Shared Encoder. 83 | self.conv1a = torch.nn.Conv2d(1, c1, kernel_size=3, stride=1, padding=1) 84 | self.conv1b = torch.nn.Conv2d(c1, c1, kernel_size=3, stride=1, padding=1) 85 | self.conv2a = torch.nn.Conv2d(c1, c2, kernel_size=3, stride=1, padding=1) 86 | self.conv2b = torch.nn.Conv2d(c2, c2, kernel_size=3, stride=1, padding=1) 87 | self.conv3a = torch.nn.Conv2d(c2, c3, kernel_size=3, stride=1, padding=1) 88 | self.conv3b = torch.nn.Conv2d(c3, c3, kernel_size=3, stride=1, padding=1) 89 | self.conv4a = torch.nn.Conv2d(c3, c4, kernel_size=3, stride=1, padding=1) 90 | self.conv4b = torch.nn.Conv2d(c4, c4, kernel_size=3, stride=1, padding=1) 91 | # Detector Head. 92 | self.convPa = torch.nn.Conv2d(c4, c5, kernel_size=3, stride=1, padding=1) 93 | self.convPb = torch.nn.Conv2d(c5, 65, kernel_size=1, stride=1, padding=0) 94 | # Descriptor Head. 95 | self.convDa = torch.nn.Conv2d(c4, c5, kernel_size=3, stride=1, padding=1) 96 | self.convDb = torch.nn.Conv2d(c5, d1, kernel_size=1, stride=1, padding=0) 97 | 98 | def forward(self, x): 99 | """ Forward pass that jointly computes unprocessed point and descriptor 100 | tensors. 101 | Input 102 | x: Image pytorch tensor shaped N x 1 x H x W. 103 | Output 104 | semi: Output point pytorch tensor shaped N x 65 x H/8 x W/8. 105 | desc: Output descriptor pytorch tensor shaped N x 256 x H/8 x W/8. 106 | """ 107 | # Shared Encoder. 108 | x = self.relu(self.conv1a(x)) 109 | x = self.relu(self.conv1b(x)) 110 | x = self.pool(x) 111 | x = self.relu(self.conv2a(x)) 112 | x = self.relu(self.conv2b(x)) 113 | x = self.pool(x) 114 | x = self.relu(self.conv3a(x)) 115 | x = self.relu(self.conv3b(x)) 116 | x = self.pool(x) 117 | x = self.relu(self.conv4a(x)) 118 | x = self.relu(self.conv4b(x)) 119 | # Detector Head. 120 | cPa = self.relu(self.convPa(x)) 121 | semi = self.convPb(cPa) 122 | # Descriptor Head. 123 | cDa = self.relu(self.convDa(x)) 124 | desc = self.convDb(cDa) 125 | dn = torch.norm(desc, p=2, dim=1) # Compute the norm. 126 | desc = desc.div(torch.unsqueeze(dn, 1)) # Divide by norm to normalize. 127 | return semi, desc 128 | 129 | 130 | class SuperPointFrontend(object): 131 | """ Wrapper around pytorch net to help with pre and post image processing. """ 132 | def __init__(self, weights_path, nms_dist, conf_thresh, nn_thresh, 133 | cuda=False): 134 | self.name = 'SuperPoint' 135 | self.cuda = cuda 136 | self.nms_dist = nms_dist 137 | self.conf_thresh = conf_thresh 138 | self.nn_thresh = nn_thresh # L2 descriptor distance for good match. 139 | self.cell = 8 # Size of each output cell. Keep this fixed. 140 | self.border_remove = 4 # Remove points this close to the border. 141 | 142 | # Load the network in inference mode. 143 | self.net = SuperPointNet() 144 | if cuda: 145 | # Train on GPU, deploy on GPU. 146 | self.net.load_state_dict(torch.load(weights_path)) 147 | self.net = self.net.cuda() 148 | else: 149 | # Train on GPU, deploy on CPU. 150 | self.net.load_state_dict(torch.load(weights_path, 151 | map_location=lambda storage, loc: storage)) 152 | self.net.eval() 153 | 154 | def nms_fast(self, in_corners, H, W, dist_thresh): 155 | """ 156 | Run a faster approximate Non-Max-Suppression on numpy corners shaped: 157 | 3xN [x_i,y_i,conf_i]^T 158 | 159 | Algo summary: Create a grid sized HxW. Assign each corner location a 1, rest 160 | are zeros. Iterate through all the 1's and convert them either to -1 or 0. 161 | Suppress points by setting nearby values to 0. 162 | 163 | Grid Value Legend: 164 | -1 : Kept. 165 | 0 : Empty or suppressed. 166 | 1 : To be processed (converted to either kept or supressed). 167 | 168 | NOTE: The NMS first rounds points to integers, so NMS distance might not 169 | be exactly dist_thresh. It also assumes points are within image boundaries. 170 | 171 | Inputs 172 | in_corners - 3xN numpy array with corners [x_i, y_i, confidence_i]^T. 173 | H - Image height. 174 | W - Image width. 175 | dist_thresh - Distance to suppress, measured as an infinty norm distance. 176 | Returns 177 | nmsed_corners - 3xN numpy matrix with surviving corners. 178 | nmsed_inds - N length numpy vector with surviving corner indices. 179 | """ 180 | grid = np.zeros((H, W)).astype(int) # Track NMS data. 181 | inds = np.zeros((H, W)).astype(int) # Store indices of points. 182 | # Sort by confidence and round to nearest int. 183 | inds1 = np.argsort(-in_corners[2,:]) 184 | corners = in_corners[:,inds1] 185 | rcorners = corners[:2,:].round().astype(int) # Rounded corners. 186 | # Check for edge case of 0 or 1 corners. 187 | if rcorners.shape[1] == 0: 188 | return np.zeros((3,0)).astype(int), np.zeros(0).astype(int) 189 | if rcorners.shape[1] == 1: 190 | out = np.vstack((rcorners, in_corners[2])).reshape(3,1) 191 | return out, np.zeros((1)).astype(int) 192 | # Initialize the grid. 193 | for i, rc in enumerate(rcorners.T): 194 | grid[rcorners[1,i], rcorners[0,i]] = 1 195 | inds[rcorners[1,i], rcorners[0,i]] = i 196 | # Pad the border of the grid, so that we can NMS points near the border. 197 | pad = dist_thresh 198 | grid = np.pad(grid, ((pad,pad), (pad,pad)), mode='constant') 199 | # Iterate through points, highest to lowest conf, suppress neighborhood. 200 | count = 0 201 | for i, rc in enumerate(rcorners.T): 202 | # Account for top and left padding. 203 | pt = (rc[0]+pad, rc[1]+pad) 204 | if grid[pt[1], pt[0]] == 1: # If not yet suppressed. 205 | grid[pt[1]-pad:pt[1]+pad+1, pt[0]-pad:pt[0]+pad+1] = 0 206 | grid[pt[1], pt[0]] = -1 207 | count += 1 208 | # Get all surviving -1's and return sorted array of remaining corners. 209 | keepy, keepx = np.where(grid==-1) 210 | keepy, keepx = keepy - pad, keepx - pad 211 | inds_keep = inds[keepy, keepx] 212 | out = corners[:, inds_keep] 213 | values = out[-1, :] 214 | inds2 = np.argsort(-values) 215 | out = out[:, inds2] 216 | out_inds = inds1[inds_keep[inds2]] 217 | return out, out_inds 218 | 219 | def run(self, img): 220 | """ Process a numpy image to extract points and descriptors. 221 | Input 222 | img - HxW numpy float32 input image in range [0,1]. 223 | Output 224 | corners - 3xN numpy array with corners [x_i, y_i, confidence_i]^T. 225 | desc - 256xN numpy array of corresponding unit normalized descriptors. 226 | heatmap - HxW numpy heatmap in range [0,1] of point confidences. 227 | """ 228 | assert img.ndim == 2, 'Image must be grayscale.' 229 | assert img.dtype == np.float32, 'Image must be float32.' 230 | H, W = img.shape[0], img.shape[1] 231 | inp = img.copy() 232 | inp = (inp.reshape(1, H, W)) 233 | inp = torch.from_numpy(inp) 234 | inp = torch.autograd.Variable(inp).view(1, 1, H, W) 235 | if self.cuda: 236 | inp = inp.cuda() 237 | # Forward pass of network. 238 | outs = self.net.forward(inp) 239 | semi, coarse_desc = outs[0], outs[1] 240 | # Convert pytorch -> numpy. 241 | semi = semi.data.cpu().numpy().squeeze() 242 | # --- Process points. 243 | dense = np.exp(semi) # Softmax. 244 | dense = dense / (np.sum(dense, axis=0)+.00001) # Should sum to 1. 245 | # Remove dustbin. 246 | nodust = dense[:-1, :, :] 247 | # Reshape to get full resolution heatmap. 248 | Hc = int(H / self.cell) 249 | Wc = int(W / self.cell) 250 | nodust = nodust.transpose(1, 2, 0) 251 | heatmap = np.reshape(nodust, [Hc, Wc, self.cell, self.cell]) 252 | heatmap = np.transpose(heatmap, [0, 2, 1, 3]) 253 | heatmap = np.reshape(heatmap, [Hc*self.cell, Wc*self.cell]) 254 | xs, ys = np.where(heatmap >= self.conf_thresh) # Confidence threshold. 255 | if len(xs) == 0: 256 | return np.zeros((3, 0)), None, None 257 | pts = np.zeros((3, len(xs))) # Populate point data sized 3xN. 258 | pts[0, :] = ys 259 | pts[1, :] = xs 260 | pts[2, :] = heatmap[xs, ys] 261 | pts, _ = self.nms_fast(pts, H, W, dist_thresh=self.nms_dist) # Apply NMS. 262 | inds = np.argsort(pts[2,:]) 263 | pts = pts[:,inds[::-1]] # Sort by confidence. 264 | # Remove points along border. 265 | bord = self.border_remove 266 | toremoveW = np.logical_or(pts[0, :] < bord, pts[0, :] >= (W-bord)) 267 | toremoveH = np.logical_or(pts[1, :] < bord, pts[1, :] >= (H-bord)) 268 | toremove = np.logical_or(toremoveW, toremoveH) 269 | pts = pts[:, ~toremove] 270 | # --- Process descriptor. 271 | D = coarse_desc.shape[1] 272 | if pts.shape[1] == 0: 273 | desc = np.zeros((D, 0)) 274 | else: 275 | # Interpolate into descriptor map using 2D point locations. 276 | samp_pts = torch.from_numpy(pts[:2, :].copy()) 277 | samp_pts[0, :] = (samp_pts[0, :] / (float(W)/2.)) - 1. 278 | samp_pts[1, :] = (samp_pts[1, :] / (float(H)/2.)) - 1. 279 | samp_pts = samp_pts.transpose(0, 1).contiguous() 280 | samp_pts = samp_pts.view(1, 1, -1, 2) 281 | samp_pts = samp_pts.float() 282 | if self.cuda: 283 | samp_pts = samp_pts.cuda() 284 | desc = torch.nn.functional.grid_sample(coarse_desc, samp_pts) 285 | desc = desc.data.cpu().numpy().reshape(D, -1) 286 | desc /= np.linalg.norm(desc, axis=0)[np.newaxis, :] 287 | return pts, desc, heatmap 288 | 289 | 290 | class PointTracker(object): 291 | """ Class to manage a fixed memory of points and descriptors that enables 292 | sparse optical flow point tracking. 293 | 294 | Internally, the tracker stores a 'tracks' matrix sized M x (2+L), of M 295 | tracks with maximum length L, where each row corresponds to: 296 | row_m = [track_id_m, avg_desc_score_m, point_id_0_m, ..., point_id_L-1_m]. 297 | """ 298 | 299 | def __init__(self, max_length, nn_thresh): 300 | if max_length < 2: 301 | raise ValueError('max_length must be greater than or equal to 2.') 302 | self.maxl = max_length 303 | self.nn_thresh = nn_thresh 304 | self.all_pts = [] 305 | for n in range(self.maxl): 306 | self.all_pts.append(np.zeros((2, 0))) 307 | self.last_desc = None 308 | self.tracks = np.zeros((0, self.maxl+2)) 309 | self.track_count = 0 310 | self.max_score = 9999 311 | 312 | def nn_match_two_way(self, desc1, desc2, nn_thresh): 313 | """ 314 | Performs two-way nearest neighbor matching of two sets of descriptors, such 315 | that the NN match from descriptor A->B must equal the NN match from B->A. 316 | 317 | Inputs: 318 | desc1 - NxM numpy matrix of N corresponding M-dimensional descriptors. 319 | desc2 - NxM numpy matrix of N corresponding M-dimensional descriptors. 320 | nn_thresh - Optional descriptor distance below which is a good match. 321 | 322 | Returns: 323 | matches - 3xL numpy array, of L matches, where L <= N and each column i is 324 | a match of two descriptors, d_i in image 1 and d_j' in image 2: 325 | [d_i index, d_j' index, match_score]^T 326 | """ 327 | assert desc1.shape[0] == desc2.shape[0] 328 | if desc1.shape[1] == 0 or desc2.shape[1] == 0: 329 | return np.zeros((3, 0)) 330 | if nn_thresh < 0.0: 331 | raise ValueError('\'nn_thresh\' should be non-negative') 332 | # Compute L2 distance. Easy since vectors are unit normalized. 333 | dmat = np.dot(desc1.T, desc2) 334 | dmat = np.sqrt(2-2*np.clip(dmat, -1, 1)) 335 | # Get NN indices and scores. 336 | idx = np.argmin(dmat, axis=1) 337 | scores = dmat[np.arange(dmat.shape[0]), idx] 338 | # Threshold the NN matches. 339 | keep = scores < nn_thresh 340 | # Check if nearest neighbor goes both directions and keep those. 341 | idx2 = np.argmin(dmat, axis=0) 342 | keep_bi = np.arange(len(idx)) == idx2[idx] 343 | keep = np.logical_and(keep, keep_bi) 344 | idx = idx[keep] 345 | scores = scores[keep] 346 | # Get the surviving point indices. 347 | m_idx1 = np.arange(desc1.shape[1])[keep] 348 | m_idx2 = idx 349 | # Populate the final 3xN match data structure. 350 | matches = np.zeros((3, int(keep.sum()))) 351 | matches[0, :] = m_idx1 352 | matches[1, :] = m_idx2 353 | matches[2, :] = scores 354 | return matches 355 | 356 | def get_offsets(self): 357 | """ Iterate through list of points and accumulate an offset value. Used to 358 | index the global point IDs into the list of points. 359 | 360 | Returns 361 | offsets - N length array with integer offset locations. 362 | """ 363 | # Compute id offsets. 364 | offsets = [] 365 | offsets.append(0) 366 | for i in range(len(self.all_pts)-1): # Skip last camera size, not needed. 367 | offsets.append(self.all_pts[i].shape[1]) 368 | offsets = np.array(offsets) 369 | offsets = np.cumsum(offsets) 370 | return offsets 371 | 372 | def update(self, pts, desc): 373 | """ Add a new set of point and descriptor observations to the tracker. 374 | 375 | Inputs 376 | pts - 3xN numpy array of 2D point observations. 377 | desc - DxN numpy array of corresponding D dimensional descriptors. 378 | """ 379 | if pts is None or desc is None: 380 | print('PointTracker: Warning, no points were added to tracker.') 381 | return 382 | assert pts.shape[1] == desc.shape[1] 383 | # Initialize last_desc. 384 | if self.last_desc is None: 385 | self.last_desc = np.zeros((desc.shape[0], 0)) 386 | # Remove oldest points, store its size to update ids later. 387 | remove_size = self.all_pts[0].shape[1] 388 | self.all_pts.pop(0) 389 | self.all_pts.append(pts) 390 | # Remove oldest point in track. 391 | self.tracks = np.delete(self.tracks, 2, axis=1) 392 | # Update track offsets. 393 | for i in range(2, self.tracks.shape[1]): 394 | self.tracks[:, i] -= remove_size 395 | self.tracks[:, 2:][self.tracks[:, 2:] < -1] = -1 396 | offsets = self.get_offsets() 397 | # Add a new -1 column. 398 | self.tracks = np.hstack((self.tracks, -1*np.ones((self.tracks.shape[0], 1)))) 399 | # Try to append to existing tracks. 400 | matched = np.zeros((pts.shape[1])).astype(bool) 401 | matches = self.nn_match_two_way(self.last_desc, desc, self.nn_thresh) 402 | for match in matches.T: 403 | # Add a new point to it's matched track. 404 | id1 = int(match[0]) + offsets[-2] 405 | id2 = int(match[1]) + offsets[-1] 406 | found = np.argwhere(self.tracks[:, -2] == id1) 407 | if found.shape[0] > 0: 408 | matched[int(match[1])] = True 409 | row = int(found) 410 | self.tracks[row, -1] = id2 411 | if self.tracks[row, 1] == self.max_score: 412 | # Initialize track score. 413 | self.tracks[row, 1] = match[2] 414 | else: 415 | # Update track score with running average. 416 | # NOTE(dd): this running average can contain scores from old matches 417 | # not contained in last max_length track points. 418 | track_len = (self.tracks[row, 2:] != -1).sum() - 1. 419 | frac = 1. / float(track_len) 420 | self.tracks[row, 1] = (1.-frac)*self.tracks[row, 1] + frac*match[2] 421 | # Add unmatched tracks. 422 | new_ids = np.arange(pts.shape[1]) + offsets[-1] 423 | new_ids = new_ids[~matched] 424 | new_tracks = -1*np.ones((new_ids.shape[0], self.maxl + 2)) 425 | new_tracks[:, -1] = new_ids 426 | new_num = new_ids.shape[0] 427 | new_trackids = self.track_count + np.arange(new_num) 428 | new_tracks[:, 0] = new_trackids 429 | new_tracks[:, 1] = self.max_score*np.ones(new_ids.shape[0]) 430 | self.tracks = np.vstack((self.tracks, new_tracks)) 431 | self.track_count += new_num # Update the track count. 432 | # Remove empty tracks. 433 | keep_rows = np.any(self.tracks[:, 2:] >= 0, axis=1) 434 | self.tracks = self.tracks[keep_rows, :] 435 | # Store the last descriptors. 436 | self.last_desc = desc.copy() 437 | return 438 | 439 | def get_tracks(self, min_length): 440 | """ Retrieve point tracks of a given minimum length. 441 | Input 442 | min_length - integer >= 1 with minimum track length 443 | Output 444 | returned_tracks - M x (2+L) sized matrix storing track indices, where 445 | M is the number of tracks and L is the maximum track length. 446 | """ 447 | if min_length < 1: 448 | raise ValueError('\'min_length\' too small.') 449 | valid = np.ones((self.tracks.shape[0])).astype(bool) 450 | good_len = np.sum(self.tracks[:, 2:] != -1, axis=1) >= min_length 451 | # Remove tracks which do not have an observation in most recent frame. 452 | not_headless = (self.tracks[:, -1] != -1) 453 | keepers = np.logical_and.reduce((valid, good_len, not_headless)) 454 | returned_tracks = self.tracks[keepers, :].copy() 455 | return returned_tracks 456 | 457 | def draw_tracks(self, out, tracks): 458 | """ Visualize tracks all overlayed on a single image. 459 | Inputs 460 | out - numpy uint8 image sized HxWx3 upon which tracks are overlayed. 461 | tracks - M x (2+L) sized matrix storing track info. 462 | """ 463 | # Store the number of points per camera. 464 | pts_mem = self.all_pts 465 | N = len(pts_mem) # Number of cameras/images. 466 | # Get offset ids needed to reference into pts_mem. 467 | offsets = self.get_offsets() 468 | # Width of track and point circles to be drawn. 469 | stroke = 1 470 | # Iterate through each track and draw it. 471 | for track in tracks: 472 | clr = myjet[int(np.clip(np.floor(track[1]*10), 0, 9)), :]*255 473 | for i in range(N-1): 474 | if track[i+2] == -1 or track[i+3] == -1: 475 | continue 476 | offset1 = offsets[i] 477 | offset2 = offsets[i+1] 478 | idx1 = int(track[i+2]-offset1) 479 | idx2 = int(track[i+3]-offset2) 480 | pt1 = pts_mem[i][:2, idx1] 481 | pt2 = pts_mem[i+1][:2, idx2] 482 | p1 = (int(round(pt1[0])), int(round(pt1[1]))) 483 | p2 = (int(round(pt2[0])), int(round(pt2[1]))) 484 | cv2.line(out, p1, p2, clr, thickness=stroke, lineType=16) 485 | # Draw end points of each track. 486 | if i == N-2: 487 | clr2 = (255, 0, 0) 488 | cv2.circle(out, p2, stroke, clr2, -1, lineType=16) 489 | 490 | class VideoStreamer(object): 491 | """ Class to help process image streams. Three types of possible inputs:" 492 | 1.) USB Webcam. 493 | 2.) A directory of images (files in directory matching 'img_glob'). 494 | 3.) A video file, such as an .mp4 or .avi file. 495 | """ 496 | def __init__(self, basedir, camid, height, width, skip, img_glob): 497 | self.cap = [] 498 | self.camera = False 499 | self.video_file = False 500 | self.listing = [] 501 | self.sizer = [height, width] 502 | self.i = 0 503 | self.skip = skip 504 | self.maxlen = 1000000 505 | # If the "basedir" string is the word camera, then use a webcam. 506 | if basedir == "camera/" or basedir == "camera": 507 | print('==> Processing Webcam Input.') 508 | self.cap = cv2.VideoCapture(camid) 509 | self.listing = range(0, self.maxlen) 510 | self.camera = True 511 | else: 512 | # Try to open as a video. 513 | self.cap = cv2.VideoCapture(basedir) 514 | lastbit = basedir[-4:len(basedir)] 515 | if (type(self.cap) == list or not self.cap.isOpened()) and (lastbit == '.mp4'): 516 | raise IOError('Cannot open movie file') 517 | elif type(self.cap) != list and self.cap.isOpened() and (lastbit != '.txt'): 518 | print('==> Processing Video Input.') 519 | num_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) 520 | self.listing = range(0, num_frames) 521 | self.listing = self.listing[::self.skip] 522 | self.camera = True 523 | self.video_file = True 524 | self.maxlen = len(self.listing) 525 | else: 526 | print('==> Processing Image Directory Input.') 527 | search = os.path.join(basedir, img_glob) 528 | self.listing = glob.glob(search) 529 | self.listing.sort() 530 | self.listing = self.listing[::self.skip] 531 | self.maxlen = len(self.listing) 532 | if self.maxlen == 0: 533 | raise IOError('No images were found (maybe bad \'--img_glob\' parameter?)') 534 | 535 | def read_image(self, impath, img_size): 536 | """ Read image as grayscale and resize to img_size. 537 | Inputs 538 | impath: Path to input image. 539 | img_size: (W, H) tuple specifying resize size. 540 | Returns 541 | grayim: float32 numpy array sized H x W with values in range [0, 1]. 542 | """ 543 | grayim = cv2.imread(impath, 0) 544 | if grayim is None: 545 | raise Exception('Error reading image %s' % impath) 546 | # Image is resized via opencv. 547 | interp = cv2.INTER_AREA 548 | grayim = cv2.resize(grayim, (img_size[1], img_size[0]), interpolation=interp) 549 | grayim = (grayim.astype('float32') / 255.) 550 | return grayim 551 | 552 | def next_frame(self): 553 | """ Return the next frame, and increment internal counter. 554 | Returns 555 | image: Next H x W image. 556 | status: True or False depending whether image was loaded. 557 | """ 558 | if self.i == self.maxlen: 559 | return (None, False) 560 | if self.camera: 561 | ret, input_image = self.cap.read() 562 | if ret is False: 563 | print('VideoStreamer: Cannot get image from camera (maybe bad --camid?)') 564 | return (None, False) 565 | if self.video_file: 566 | self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.listing[self.i]) 567 | input_image = cv2.resize(input_image, (self.sizer[1], self.sizer[0]), 568 | interpolation=cv2.INTER_AREA) 569 | input_image = cv2.cvtColor(input_image, cv2.COLOR_RGB2GRAY) 570 | input_image = input_image.astype('float')/255.0 571 | else: 572 | image_file = self.listing[self.i] 573 | input_image = self.read_image(image_file, self.sizer) 574 | # Increment internal counter. 575 | self.i = self.i + 1 576 | input_image = input_image.astype('float32') 577 | return (input_image, True) 578 | 579 | def writeSPtofile(pts, desc,sizeratio, filename_pre, maxpoints = 1000): 580 | maxlen = min( pts.shape[1] , maxpoints) 581 | pts = pts[:,0:maxlen] 582 | outpts_x = pts[0]*sizeratio[0] 583 | outpts_y = pts[1]*sizeratio[1] 584 | desc = desc[:,0:maxlen] 585 | with open(filename_pre+'_kpts.txt','w') as outfile: 586 | for index in range(0,len(outpts_x) ): 587 | print('{x} {y}'.format( x=int(outpts_x[index]),y=int(outpts_y[index]) ),file=outfile) 588 | with open(filename_pre+'_feats.txt','w') as outfile: 589 | for index in range(0,desc.shape[1] ): 590 | for element in desc[:,index]: 591 | print(element,file=outfile,end=' ') 592 | print('',file=outfile) 593 | 594 | 595 | def getImage2SP(img_name, filename_pre, fe): 596 | grayim = cv2.imread(img_name,0) 597 | ori_size = grayim.shape 598 | interp = cv2.INTER_AREA 599 | grayim = cv2.resize(grayim, ( 480 , 640 ), interpolation=interp) 600 | sizeratio = (ori_size[0]/480 , ori_size[1]/640) 601 | grayim = (grayim.astype('float32') / 255.) 602 | pts, desc, _ = fe.run(grayim) 603 | writeSPtofile(pts,desc,sizeratio,filename_pre) 604 | tmp = 1 605 | 606 | def test2SP(img_name, fe): 607 | grayim = cv2.imread(img_name,0) 608 | ori_size = grayim.shape 609 | interp = cv2.INTER_AREA 610 | grayim = cv2.resize(grayim, ( 480 , 640 ), interpolation=interp) 611 | sizeratio = (ori_size[0]/480 , ori_size[1]/640) 612 | grayim = (grayim.astype('float32') / 255.) 613 | pts, desc, _ = fe.run(grayim) 614 | maxlen = min( pts.shape[1] , 200) 615 | pts = pts[:,0:maxlen] 616 | outpts_x = pts[0]*sizeratio[0] 617 | outpts_y = pts[1]*sizeratio[1] 618 | desc = desc[:,0:maxlen] 619 | 620 | for index in range(0,len(outpts_x) ): 621 | cv2.circle(grayim, (int(outpts_x[index]),int(outpts_y[index])), 1, (0, 255, 0), -1, lineType=16 ) 622 | cv2.imshow("Test",grayim) 623 | cv2.waitKey() 624 | 625 | 626 | tmp = 1 627 | 628 | if __name__ == '__main__': 629 | fe = SuperPointFrontend(weights_path='superpoint_v1.pth', 630 | nms_dist=4, 631 | conf_thresh=0.005, 632 | nn_thresh=0.7, 633 | cuda=True) 634 | print('==> Successfully loaded pre-trained network.') 635 | 636 | listenlingfolder = '/dev/shm/tmpSP/' 637 | 638 | # test2SP('test.png',fe) 639 | 640 | while(True): 641 | while ( not os.path.exists(listenlingfolder+'ready.txt') ): 642 | time.sleep(0.01) 643 | # print("1234") 644 | os.remove(listenlingfolder+'ready.txt') 645 | f_list = glob.glob( listenlingfolder+'/*.png' ) 646 | assert len(f_list) == 1 647 | for file_i in f_list: 648 | if file_i.split('.')[-1] == 'png': 649 | print(file_i) 650 | targetpre = re.sub(r'\.png', "", file_i) 651 | print(targetpre) 652 | getImage2SP(file_i,targetpre,fe) 653 | os.remove(file_i) 654 | pathlib.Path(listenlingfolder + '/done.txt').touch() 655 | while ( os.path.exists(listenlingfolder+'/done.txt') ): 656 | time.sleep(0.01) 657 | # print("4321") 658 | 659 | 660 | 661 | 662 | if __name__ == '__main__12': 663 | 664 | # Parse command line arguments. 665 | parser = argparse.ArgumentParser(description='PyTorch SuperPoint Demo.') 666 | parser.add_argument('input', type=str, default='', 667 | help='Image directory or movie file or "camera" (for webcam).') 668 | parser.add_argument('--weights_path', type=str, default='superpoint_v1.pth', 669 | help='Path to pretrained weights file (default: superpoint_v1.pth).') 670 | parser.add_argument('--img_glob', type=str, default='*.png', 671 | help='Glob match if directory of images is specified (default: \'*.png\').') 672 | parser.add_argument('--skip', type=int, default=1, 673 | help='Images to skip if input is movie or directory (default: 1).') 674 | parser.add_argument('--show_extra', action='store_true', 675 | help='Show extra debug outputs (default: False).') 676 | parser.add_argument('--H', type=int, default=120, 677 | help='Input image height (default: 120).') 678 | parser.add_argument('--W', type=int, default=160, 679 | help='Input image width (default:160).') 680 | parser.add_argument('--display_scale', type=int, default=2, 681 | help='Factor to scale output visualization (default: 2).') 682 | parser.add_argument('--min_length', type=int, default=2, 683 | help='Minimum length of point tracks (default: 2).') 684 | parser.add_argument('--max_length', type=int, default=5, 685 | help='Maximum length of point tracks (default: 5).') 686 | parser.add_argument('--nms_dist', type=int, default=4, 687 | help='Non Maximum Suppression (NMS) distance (default: 4).') 688 | parser.add_argument('--conf_thresh', type=float, default=0.015, 689 | help='Detector confidence threshold (default: 0.015).') 690 | parser.add_argument('--nn_thresh', type=float, default=0.7, 691 | help='Descriptor matching threshold (default: 0.7).') 692 | parser.add_argument('--camid', type=int, default=0, 693 | help='OpenCV webcam video capture ID, usually 0 or 1 (default: 0).') 694 | parser.add_argument('--waitkey', type=int, default=1, 695 | help='OpenCV waitkey time in ms (default: 1).') 696 | parser.add_argument('--cuda', action='store_true', 697 | help='Use cuda GPU to speed up network processing speed (default: False)') 698 | parser.add_argument('--no_display', action='store_true', 699 | help='Do not display images to screen. Useful if running remotely (default: False).') 700 | parser.add_argument('--write', action='store_true', 701 | help='Save output frames to a directory (default: False)') 702 | parser.add_argument('--write_dir', type=str, default='tracker_outputs/', 703 | help='Directory where to write output frames (default: tracker_outputs/).') 704 | opt = parser.parse_args() 705 | print(opt) 706 | 707 | # This class helps load input images from different sources. 708 | vs = VideoStreamer(opt.input, opt.camid, opt.H, opt.W, opt.skip, opt.img_glob) 709 | 710 | print('==> Loading pre-trained network.') 711 | # This class runs the SuperPoint network and processes its outputs. 712 | fe = SuperPointFrontend(weights_path=opt.weights_path, 713 | nms_dist=opt.nms_dist, 714 | conf_thresh=opt.conf_thresh, 715 | nn_thresh=opt.nn_thresh, 716 | cuda=opt.cuda) 717 | print('==> Successfully loaded pre-trained network.') 718 | 719 | # This class helps merge consecutive point matches into tracks. 720 | tracker = PointTracker(opt.max_length, nn_thresh=fe.nn_thresh) 721 | 722 | # Create a window to display the demo. 723 | if not opt.no_display: 724 | win = 'SuperPoint Tracker' 725 | cv2.namedWindow(win) 726 | else: 727 | print('Skipping visualization, will not show a GUI.') 728 | 729 | # Font parameters for visualizaton. 730 | font = cv2.FONT_HERSHEY_DUPLEX 731 | font_clr = (255, 255, 255) 732 | font_pt = (4, 12) 733 | font_sc = 0.4 734 | 735 | # Create output directory if desired. 736 | if opt.write: 737 | print('==> Will write outputs to %s' % opt.write_dir) 738 | if not os.path.exists(opt.write_dir): 739 | os.makedirs(opt.write_dir) 740 | 741 | print('==> Running Demo.') 742 | while True: 743 | 744 | start = time.time() 745 | 746 | # Get a new image. 747 | img, status = vs.next_frame() 748 | if status is False: 749 | break 750 | 751 | # Get points and descriptors. 752 | start1 = time.time() 753 | pts, desc, heatmap = fe.run(img) 754 | end1 = time.time() 755 | 756 | # Add points and descriptors to the tracker. 757 | tracker.update(pts, desc) 758 | 759 | # Get tracks for points which were match successfully across all frames. 760 | tracks = tracker.get_tracks(opt.min_length) 761 | 762 | # Primary output - Show point tracks overlayed on top of input image. 763 | out1 = (np.dstack((img, img, img)) * 255.).astype('uint8') 764 | tracks[:, 1] /= float(fe.nn_thresh) # Normalize track scores to [0,1]. 765 | tracker.draw_tracks(out1, tracks) 766 | if opt.show_extra: 767 | cv2.putText(out1, 'Point Tracks', font_pt, font, font_sc, font_clr, lineType=16) 768 | 769 | # Extra output -- Show current point detections. 770 | out2 = (np.dstack((img, img, img)) * 255.).astype('uint8') 771 | for pt in pts.T: 772 | pt1 = (int(round(pt[0])), int(round(pt[1]))) 773 | cv2.circle(out2, pt1, 1, (0, 255, 0), -1, lineType=16) 774 | cv2.putText(out2, 'Raw Point Detections', font_pt, font, font_sc, font_clr, lineType=16) 775 | 776 | # Extra output -- Show the point confidence heatmap. 777 | if heatmap is not None: 778 | min_conf = 0.001 779 | heatmap[heatmap < min_conf] = min_conf 780 | heatmap = -np.log(heatmap) 781 | heatmap = (heatmap - heatmap.min()) / (heatmap.max() - heatmap.min() + .00001) 782 | out3 = myjet[np.round(np.clip(heatmap*10, 0, 9)).astype('int'), :] 783 | out3 = (out3*255).astype('uint8') 784 | else: 785 | out3 = np.zeros_like(out2) 786 | cv2.putText(out3, 'Raw Point Confidences', font_pt, font, font_sc, font_clr, lineType=16) 787 | 788 | # Resize final output. 789 | if opt.show_extra: 790 | out = np.hstack((out1, out2, out3)) 791 | out = cv2.resize(out, (3*opt.display_scale*opt.W, opt.display_scale*opt.H)) 792 | else: 793 | out = cv2.resize(out1, (opt.display_scale*opt.W, opt.display_scale*opt.H)) 794 | 795 | # Display visualization image to screen. 796 | if not opt.no_display: 797 | cv2.imshow(win, out) 798 | key = cv2.waitKey(opt.waitkey) & 0xFF 799 | if key == ord('q'): 800 | print('Quitting, \'q\' pressed.') 801 | break 802 | 803 | # Optionally write images to disk. 804 | if opt.write: 805 | out_file = os.path.join(opt.write_dir, 'frame_%05d.png' % vs.i) 806 | print('Writing image to %s' % out_file) 807 | cv2.imwrite(out_file, out) 808 | 809 | end = time.time() 810 | net_t = (1./ float(end1 - start)) 811 | total_t = (1./ float(end - start)) 812 | if opt.show_extra: 813 | print('Processed image %d (net+post_process: %.2f FPS, total: %.2f FPS).'\ 814 | % (vs.i, net_t, total_t)) 815 | 816 | # Close any remaining windows. 817 | cv2.destroyAllWindows() 818 | 819 | print('==> Finshed Demo.') 820 | -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/superpoint_v1.pth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/superpoint_v1.pth -------------------------------------------------------------------------------- /SuperPointPretrainedNetwork/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/SuperPointPretrainedNetwork/test.png -------------------------------------------------------------------------------- /modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FarawaySail/SuperPoint_SLAM/e34cc9fdf321096797f7eb8c6fce33be2f4225ef/modules.png --------------------------------------------------------------------------------