Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
925a794
added SIFT_GPU support based on pyopencl and silx packages.
Oburshan Jul 9, 2020
9fc4b9f
added some changes in matching and feature detection for better perfo…
Oburshan Jul 13, 2020
c2e31e6
added sift_gpu parameters to the config.
Oburshan Jul 26, 2020
98e9f73
changed test_sift_gpu.py file
Oburshan Jul 26, 2020
5975669
test
Oburshan Jul 26, 2020
0c97d34
changed test_sift_gpu.py file
cojosef96 Jul 26, 2020
06414ba
changed gitignore
cojosef96 Jul 26, 2020
ca8947f
deleted unnecessary things
cojosef96 Jul 26, 2020
982b13d
added information to the readme.md file
cojosef96 Jul 26, 2020
ed5abf4
added readme pictures
cojosef96 Jul 26, 2020
4a5d2b9
fixed readme
cojosef96 Jul 26, 2020
d2a700a
added readme pictures
cojosef96 Jul 27, 2020
b58b18d
changed the readme.md
cojosef96 Jul 27, 2020
2445387
Merge branch 'master' into sift_gpu
cojosef96 Jul 27, 2020
66168c3
added csfm.so
cojosef96 Jul 27, 2020
2b98651
Merge remote-tracking branch 'github_opensfm/sift_gpu' into sift_gpu
cojosef96 Jul 27, 2020
3a8c888
Update features.py
cojosef96 Jul 28, 2020
58748f9
Merge pull request #1 from cojosef96/sift_gpu_v0.4.0
cojosef96 Jul 28, 2020
3949738
Update .gitignore
cojosef96 Jul 28, 2020
2b5d059
Update features.py
cojosef96 Jul 28, 2020
4b6e49e
Merge pull request #2 from cojosef96/sift_gpu_v0.4.0
cojosef96 Jul 28, 2020
cb4a7d4
Merge pull request #3 from cojosef96/sift_gpu_v0.4.0
cojosef96 Jul 28, 2020
22367d0
Update matching.py
cojosef96 Jul 28, 2020
1092790
added try except for the case of import error of silx library
cojosef96 Aug 5, 2020
35d91a6
added function that convert p1,f1 to gpu_keypoints (k1). change the m…
cojosef96 Aug 5, 2020
a3dae4f
tested the matching implementation of SIFT_GPU now it works without e…
cojosef96 Aug 6, 2020
4958f5a
tested the import error problem
cojosef96 Aug 6, 2020
14abded
remove all debug changes, now its the same as the original repository
cojosef96 Aug 6, 2020
f9acc6b
Merge pull request #4 from cojosef96/sift_gpu_v0.4.0
cojosef96 Aug 6, 2020
f465ef0
changed the requirements and returned the original opensfm_run_all file
cojosef96 Aug 9, 2020
8c17bb2
Merge pull request #5 from cojosef96/sift_gpu_v0.4.0
cojosef96 Aug 9, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,90 @@ Checkout this [blog post with more demos](http://blog.mapillary.com/update/2014/
[Building the library]: https://docs.opensfm.org/building.html (OpenSfM building instructions)
[Running a reconstruction]: https://docs.opensfm.org/using.html (OpenSfM usage)
[Documentation]: https://docs.opensfm.org (OpenSfM documentation)

## Update Sift_GPU version

### Changes:
* added SIFT_GPU parameters to the config file.
* added SiftGPU class.
* added feature detection function in features.py file.
* added matching functions to the matching.py file.
* changed the commands to support the sift_gpu option.
* added functions to of save and load sift_gpu keypoints to the dataset.py file.



### Requirements:
This update depends on the silx python package.

To install it you will need to install pyopencl.
Please see the installation guide [Here](http://www.silx.org/doc/silx/dev/install.html).

### Usage
In order to test if all the packages were installed correctly. Try to run my test code.
Which match two of the images in the Berlin data. The code can be found in the main folder under the name "test_sift_gpu.py".

To run this Code simply write:
```
cd OpenSfm_sift_gpu
python test_sift_gpu.py
```
### Run OpenSfm

If all the dependencies were installed correctly.
To run the opensfm pipeline enter this commands:
```
cd OpenSfm_sift_gpu
./bin/opensfm_run_all data/berlin_gpu
```

To check the sfm, enter:

`python3 -m http.server`

and click on this [link](http://localhost:8000/viewer/reconstruction.html#file=/data/berlin_gpu/reconstruction.meshed.json) to see the reconstruction.

### Benchmark
Here you can see the results of the Sift_GPU implementation.

#### Feature detection
<p align="center">
<img src="misc/img1_features.png" width="400">
<img src="misc/img2_features.png" width="400">
</p>


<p align="center">
<img src="misc/img1_features_zoom.png" width="400">
<img src="misc/img2_features_zoom.png" width="400">
</p>

#### Feature Matching

<p align="center">
<img src="misc/sift_gpu_matching.png" width="800">
</p>

<p align="center">
<img src="misc/sift_matching_2.png" width="800">
</p>

#### SFM Creation

<p align="center">
<img src="misc/sfm_berlin_gpu.png" width="800">
</p>

<p align="center">
<img src="misc/sfm_berlin_2.png" width="800">
</p>

#### Speed
Feature Matching on image (3264x2448) on 1080-TI-GTX took 0.28 sec

Feature Matching took 0.025 sec

#### Contact Me

For more information, you can contact me via [Email](mailto:[email protected])

11 changes: 11 additions & 0 deletions data/berlin_gpu/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

# OpenSfM will use the default parameters from opensfm/config.py
# Set here any parameter that you want to override for this dataset
# For example:
processes: 1 # Number of threads to use, SIFT_GPU does not work on parallel
depthmap_min_consistent_views: 2 # Min number of views that should reconstruct a point for it to be valid
depthmap_save_debug_files: yes # Save debug files with partial reconstruction results
feature_process_size: -1
bundle_use_gcp: yes
feature_type: SIFT_GPU # Feature type (AKAZE, SURF, SIFT, HAHOG, ORB, SIFT_GPU)

64 changes: 64 additions & 0 deletions data/berlin_gpu/ground_control_points.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"points": [
{
"position": {
"latitude": 52.51926834404209,
"altitude": 14.946108041331172,
"longitude": 13.400703631118825
},
"id": "0",
"observations": [
{
"projection": [
-0.015689,
-0.0625362
],
"shot_id": "02.jpg"
},
{
"projection": [
-0.0624397,
0.0440716
],
"shot_id": "03.jpg"
}
]
},
{
"id": "1",
"observations": [
{
"projection": [
0.049848,
0.144291
],
"shot_id": "02.jpg"
},
{
"projection": [
0.0105317,
0.244958
],
"shot_id": "01.jpg"
}
]
},
{
"position": {
"latitude": 52.5192651808067,
"altitude": 12.859175567515194,
"longitude": 13.400764257288497
},
"id": "3",
"observations": [
{
"projection": [
0.0608681,
-0.00730461
],
"shot_id": "02.jpg"
}
]
}
]
}
Binary file added data/berlin_gpu/images/01.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/berlin_gpu/images/02.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/berlin_gpu/images/03.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/berlin_gpu/masks/01.jpg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/berlin_gpu/masks/02.jpg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/berlin_gpu/masks/03.jpg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/img1_features.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/img1_features_zoom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/img2_features.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/img2_features_zoom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/sfm_berlin_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/sfm_berlin_gpu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/sift_gpu_matching.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/sift_matching_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 12 additions & 4 deletions opensfm/commands/detect_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,17 @@ def detect(args):
data.feature_type().upper(), image))

start = timer()

p_unmasked, f_unmasked, c_unmasked = features.extract_features(
data.load_image(image), data.config)
if data.config["feature_type"] == "SIFT_GPU":
unmasked, keypoints = features.extract_features(
data.load_image(image), data.config)
p_unmasked, f_unmasked, c_unmasked = unmasked
else:
p_unmasked, f_unmasked, c_unmasked = features.extract_features(
data.load_image(image), data.config)

fmask = data.load_features_mask(image, p_unmasked)

if data.config["feature_type"] == "SIFT_GPU":
keypoints = keypoints[fmask]
p_unsorted = p_unmasked[fmask]
f_unsorted = f_unmasked[fmask]
c_unsorted = c_unmasked[fmask]
Expand All @@ -88,6 +93,9 @@ def detect(args):
p_sorted = p_unsorted[order, :]
f_sorted = f_unsorted[order, :]
c_sorted = c_unsorted[order, :]
if data.config["feature_type"] == "SIFT_GPU":
keypoints_sorted = keypoints[order]
data.save_gpu_features(image, keypoints_sorted)
data.save_features(image, p_sorted, f_sorted, c_sorted)

if need_words:
Expand Down
11 changes: 9 additions & 2 deletions opensfm/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
default_focal_prior: 0.85

# Params for features
feature_type: HAHOG # Feature type (AKAZE, SURF, SIFT, HAHOG, ORB)
feature_type: HAHOG # Feature type (AKAZE, SURF, SIFT, HAHOG, ORB, SIFT_GPU)
feature_root: 1 # If 1, apply square root mapping to features
feature_min_frames: 4000 # If fewer frames are detected, sift_peak_threshold/surf_hessian_threshold is reduced.
feature_process_size: 2048 # Resize the image if its size is larger than specified. Set to -1 for original size
Expand All @@ -17,6 +17,13 @@
sift_peak_threshold: 0.1 # Smaller value -> more features
sift_edge_threshold: 10 # See OpenCV doc

# Params for SIFT_GPU
# More information on Feature detection http://www.silx.org/doc/silx/dev/_modules/silx/opencl/sift/plan.html#SiftPlan
# More information on Feature Matching http://www.silx.org/doc/silx/dev/_modules/silx/opencl/sift/match.html
sift_gpu_init_sigma: 1.6 # blurring width, you should have good reasons to modify the 1.6 default value
pix_per_keypoints: 10 # Number of key-point pre-allocated: 1 for 10 pixel
sift_gpu_device_type: GPU # Can be 'CPU' or 'GPU'

# Params for SURF
surf_hessian_threshold: 3000 # Smaller value -> more features
surf_n_octaves: 4 # See OpenCV doc
Expand All @@ -39,7 +46,7 @@

# Params for general matching
lowes_ratio: 0.8 # Ratio test for matches
matcher_type: FLANN # FLANN, BRUTEFORCE, or WORDS
matcher_type: FLANN # FLANN, BRUTEFORCE, SIFT_GPU or WORDS
symmetric_matching: yes # Match symmetricly or one-way

# Params for FLANN matching
Expand Down
23 changes: 21 additions & 2 deletions opensfm/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class DataSet(object):
It is possible to store data remotely or in different formats
by subclassing this class and overloading its methods.
"""

def __init__(self, data_path):
"""Init dataset associated to a folder."""
self.data_path = data_path
Expand Down Expand Up @@ -316,8 +317,8 @@ def _save_features(self, filepath, points, descriptors, colors=None):
features.save_features(filepath, points, descriptors, colors, self.config)

def features_exist(self, image):
return os.path.isfile(self._feature_file(image)) or\
os.path.isfile(self._feature_file_legacy(image))
return os.path.isfile(self._feature_file(image)) or \
os.path.isfile(self._feature_file_legacy(image))

def load_features(self, image):
if os.path.isfile(self._feature_file_legacy(image)):
Expand All @@ -327,9 +328,27 @@ def load_features(self, image):
def save_features(self, image, points, descriptors, colors):
self._save_features(self._feature_file(image), points, descriptors, colors)

def save_gpu_features(self, image, keypoints):
io.mkdir_p(self._feature_path())
path = "./"+self._gpu_feature_file(image)
# Store data (serialize)
with open(path, 'wb+') as handle:
pickle.dump(keypoints, handle, protocol=pickle.HIGHEST_PROTOCOL)

def load_gpu_features(self, image):
path = self._gpu_feature_file(image)
if os.path.isfile(path):
with open(path, 'rb') as handle:
keypoints = pickle.load(handle)
return keypoints
return None

def _words_file(self, image):
return os.path.join(self._feature_path(), image + '.words.npz')

def _gpu_feature_file(self, image):
return os.path.join(self._feature_path(), image.rsplit(".")[0] + '_gpu.pkl')

def words_exist(self, image):
return os.path.isfile(self._words_file(image))

Expand Down
22 changes: 21 additions & 1 deletion opensfm/feature_loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

from opensfm import features as ft


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -103,3 +102,24 @@ def _load_features_nocache(self, data, image):
else:
points = np.array(points[:, :3], dtype=float)
return points, features, colors

def create_gpu_keypoints_from_features(self, p1, f1):
########################################################################
# Merge keypoints in central memory
########################################################################
if type(f1[0][0]) is not int:
f1 = np.uint8(f1 * 512)
total_size = len(p1)
dtype_kp = np.dtype([('x', np.float32),
('y', np.float32),
('scale', np.float32),
('angle', np.float32),
('desc', (np.uint8, 128))
])
output = np.recarray(shape=(total_size,), dtype=dtype_kp)
output[:total_size].x = p1[:, 0]
output[:total_size].y = p1[:, 1]
output[:total_size].scale = p1[:, 2]
# output[:total_size].angle = p1[:, 3]
output[:total_size].desc = f1
return output
Loading