Repository files navigation

CudaSift - SIFT features with CUDA

This is the fourth version of a SIFT (Scale Invariant Feature Transform) implementation using CUDA for GPUs from NVidia. The first version is from 2007 and GPUs have evolved since then. This version is slightly more precise and considerably faster than the previous versions and has been optimized for Kepler and later generations of GPUs.

On a GTX 1060 GPU the code takes about 1.2 ms on a 1280x960 pixel image and 1.7 ms on a 1920x1080 pixel image. There is also code for brute-force matching of features that takes about 2.2 ms for two sets of around 1900 SIFT features each.

The code relies on CMake for compilation and OpenCV for image containers. OpenCV can however be quite easily changed to something else. The code can be relatively hard to read, given the way things have been parallelized for maximum speed.

The code is free to use for non-commercial applications. If you use the code for research, please cite to the following paper.

M. Björkman, N. Bergström and D. Kragic, "Detecting, segmenting and tracking unknown objects using multi-label MRF inference", CVIU, 118, pp. 111-127, January 2014. ScienceDirect

Update in feature matching (2019-05-17)

The brute force feature matcher has been significantly improved in speed. The largest improvements can be seen for large feature sets with 10000 features or more, but as can be seen below, it performs rather well even with just 2000 features. The file match.pdf includes a description of the optimizations done in this version.

New version for Pascal (2018-10-26)

There is a new version optimized for Pascal cards, but it should work also on many older cards. Since it includes some bug fixes that changes slightly how features are extracted, which might affect matching to features extracted using an older version, the changes are kept in a new branch (Pascal). The fixes include a small change in ScaleDown that corrects an odd behaviour for images with heights not divisible by 2^(#octaves). The second change is a correction of an improper shift of (0.5,0.5) pixels, when pixel values were read from the image to create a descriptor.

Then there are some improvements in terms of speed, especially in the Laplace function, that detects DoG features, and the LowPass function, that is seen as preprocessing and is not included in the benchmarking below. Maybe surprisingly, even if optimizations were done with respect to Pascal cards, these improvements were even better for older cards. The changes involve trying to make each CUDA thread have more work to do, using fewer thread blocks. For typical images of today, there will be enough blocks to feed the streaming multiprocessors anyway.

Latest result of version under test:

1280x9601920x1080GFLOPSBandwidthMatching
TuringGeForce RTX 2080 Ti0.42*0.56*117506160.30*
PascalGeForce GTX 1080 Ti0.58*0.80*106094840.42*
PascalGeForce GTX 10601.21.738551922.2
MaxwellGeForce GTX 9701.31.834942242.5
KeplerTesla K40c2.43.442912884.7

Matching is done between two sets of 1911 and 2086 features respectively. A star indicates results from the last checked in version.

Benchmarking of new version (2018-08-22)

About every 2nd year, I try to update the code to gain even more speed through further optimization. Here are some results for a new version of the code. Improvements in speed have primarilly been gained by reducing communication between host and device, better balancing the load on caches, shared and global memory, and increasing the workload of each thread block.

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti0.71.0106094841.0
PascalGeForce GTX 10601.62.438551922.2
MaxwellGeForce GTX 9701.92.834942242.5
KeplerTesla K40c3.14.742912884.7
KeplerGeForce GTX TITAN2.94.345002884.5

Matching is done between two sets of 1818 and 1978 features respectively.

It's questionable whether further optimization really makes sense, given that the cost of just transfering an 1920x1080 pixel image to the device takes about 1.4 ms on a GTX 1080 Ti. Even if the brute force feature matcher is not much faster than earlier versions, it does not have the same O(N^2) temporary memory overhead, which is preferable if there are many features.

Benchmarking of previous version (2017-05-24)

Computational cost (in milliseconds) on different GPUs:

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti1.72.3106094841.4
PascalGeForce GTX 10602.74.038551922.6
MaxwellGeForce GTX 9703.85.634942242.8
KeplerTesla K40c5.48.042912885.5
KeplerGeForce GTX TITAN4.46.645002884.6

Matching is done between two sets of 1616 and 1769 features respectively.

The improvements in this version involved a slight adaptation for Pascal, changing from textures to global memory (mostly through L2) in the most costly function LaplaceMulti. The medium-end card GTX 1060 is impressive indeed.

Usage

There are two different containers for storing data on the host and on the device; SiftData for SIFT features and CudaImage for images. Since memory allocation on GPUs is slow, it's usually preferable to preallocate a sufficient amount of memory using InitSiftData(), in particular if SIFT features are extracted from a continuous stream of video camera images. On repeated calls ExtractSift() will reuse memory previously allocated.

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<cudaImage.h>#include<cudaSift.h>/* Reserve memory space for a whole bunch of SIFT features. */SiftDatasiftData;
InitSiftData(siftData, 25000, true, true);
/* Read image using OpenCV and convert to floating point. */cv::Matlimg;
cv::imread("image.png", 0).convertTo(limg, CV32FC1);
/* Allocate 1280x960 pixel image with device side pitch of 1280 floats. *//* Memory on host side already allocated by OpenCV is reused. */CudaImageimg;
img.Allocate(1280, 960, 1280, false, NULL, (float*) limg.data);
/* Download image from host to device */img.Download();
intnumOctaves=5; /* Number of octaves in Gaussian pyramid */floatinitBlur=1.0f; /* Amount of initial Gaussian blurring in standard deviations */floatthresh=3.5f; /* Threshold on difference of Gaussians for feature pruning */floatminScale=0.0f; /* Minimum acceptable scale to remove fine-scale features */boolupScale= false; /* Whether to upscale image before extraction *//* Extract SIFT features */ExtractSift(siftData, img, numOctaves, initBlur, thresh, minScale, upScale);
...
/* Free space allocated from SIFT features */FreeSiftData(siftData);

Parameter setting

The requirements on number and quality of features vary from application to application. Some applications benefit from a smaller number of high quality features, while others require as many features as possible. More distinct features with higher DoG (difference of Gaussians) responses tend to be of higher quality and are easier to match between multiple views. With the parameter thresh a threshold can be set on the minimum DoG to prune features of less quality.

In many cases the most fine-scale features are of little use, especially when noise conditions are severe or when features are matched between very different views. In such cases the most fine-scale features can be pruned by setting minScale to the minimum acceptable feature scale, where 1.0 corresponds to the original image scale without upscaling. As a consequence of pruning the computational cost can also be reduced.

To increase the number of SIFT features, but also increase the computational cost, the original image can be automatically upscaled to double the size using the upScale parameter, in accordance to Lowe's recommendations. One should keep in mind though that by doing so the fraction of features that can be matched tend to go down, even if the total number of extracted features increases significantly. If it's enough to instead reduce the thresh parameter to get more features, that is often a better alternative.

Results without upscaling (upScale=False) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
1.0423640.4%5.8
1.5349142.5%5.2
2.0272043.2%4.7
2.5212144.4%4.2
3.0162745.8%3.9
3.5118946.2%3.6
4.088148.5%3.3

Results with upscaling (upScale=True) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
2.0450234.9%13.2
2.5338935.9%11.2
3.0252937.1%10.6
3.5184138.3%9.9
4.0133139.8%9.5
4.595442.2%9.3
5.061139.3%9.1

About

A CUDA implementation of SIFT for NVidia GPUs (1.2 ms on a GTX 1060)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

CudaSift - SIFT features with CUDA

This is the fourth version of a SIFT (Scale Invariant Feature Transform) implementation using CUDA for GPUs from NVidia. The first version is from 2007 and GPUs have evolved since then. This version is slightly more precise and considerably faster than the previous versions and has been optimized for Kepler and later generations of GPUs.

On a GTX 1060 GPU the code takes about 1.2 ms on a 1280x960 pixel image and 1.7 ms on a 1920x1080 pixel image. There is also code for brute-force matching of features that takes about 2.2 ms for two sets of around 1900 SIFT features each.

The code relies on CMake for compilation and OpenCV for image containers. OpenCV can however be quite easily changed to something else. The code can be relatively hard to read, given the way things have been parallelized for maximum speed.

The code is free to use for non-commercial applications. If you use the code for research, please cite to the following paper.

M. Björkman, N. Bergström and D. Kragic, "Detecting, segmenting and tracking unknown objects using multi-label MRF inference", CVIU, 118, pp. 111-127, January 2014. ScienceDirect

Update in feature matching (2019-05-17)

The brute force feature matcher has been significantly improved in speed. The largest improvements can be seen for large feature sets with 10000 features or more, but as can be seen below, it performs rather well even with just 2000 features. The file match.pdf includes a description of the optimizations done in this version.

New version for Pascal (2018-10-26)

There is a new version optimized for Pascal cards, but it should work also on many older cards. Since it includes some bug fixes that changes slightly how features are extracted, which might affect matching to features extracted using an older version, the changes are kept in a new branch (Pascal). The fixes include a small change in ScaleDown that corrects an odd behaviour for images with heights not divisible by 2^(#octaves). The second change is a correction of an improper shift of (0.5,0.5) pixels, when pixel values were read from the image to create a descriptor.

Then there are some improvements in terms of speed, especially in the Laplace function, that detects DoG features, and the LowPass function, that is seen as preprocessing and is not included in the benchmarking below. Maybe surprisingly, even if optimizations were done with respect to Pascal cards, these improvements were even better for older cards. The changes involve trying to make each CUDA thread have more work to do, using fewer thread blocks. For typical images of today, there will be enough blocks to feed the streaming multiprocessors anyway.

Latest result of version under test:

1280x9601920x1080GFLOPSBandwidthMatching
TuringGeForce RTX 2080 Ti0.42*0.56*117506160.30*
PascalGeForce GTX 1080 Ti0.58*0.80*106094840.42*
PascalGeForce GTX 10601.21.738551922.2
MaxwellGeForce GTX 9701.31.834942242.5
KeplerTesla K40c2.43.442912884.7

Matching is done between two sets of 1911 and 2086 features respectively. A star indicates results from the last checked in version.

Benchmarking of new version (2018-08-22)

About every 2nd year, I try to update the code to gain even more speed through further optimization. Here are some results for a new version of the code. Improvements in speed have primarilly been gained by reducing communication between host and device, better balancing the load on caches, shared and global memory, and increasing the workload of each thread block.

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti0.71.0106094841.0
PascalGeForce GTX 10601.62.438551922.2
MaxwellGeForce GTX 9701.92.834942242.5
KeplerTesla K40c3.14.742912884.7
KeplerGeForce GTX TITAN2.94.345002884.5

Matching is done between two sets of 1818 and 1978 features respectively.

It's questionable whether further optimization really makes sense, given that the cost of just transfering an 1920x1080 pixel image to the device takes about 1.4 ms on a GTX 1080 Ti. Even if the brute force feature matcher is not much faster than earlier versions, it does not have the same O(N^2) temporary memory overhead, which is preferable if there are many features.

Benchmarking of previous version (2017-05-24)

Computational cost (in milliseconds) on different GPUs:

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti1.72.3106094841.4
PascalGeForce GTX 10602.74.038551922.6
MaxwellGeForce GTX 9703.85.634942242.8
KeplerTesla K40c5.48.042912885.5
KeplerGeForce GTX TITAN4.46.645002884.6

Matching is done between two sets of 1616 and 1769 features respectively.

The improvements in this version involved a slight adaptation for Pascal, changing from textures to global memory (mostly through L2) in the most costly function LaplaceMulti. The medium-end card GTX 1060 is impressive indeed.

Usage

There are two different containers for storing data on the host and on the device; SiftData for SIFT features and CudaImage for images. Since memory allocation on GPUs is slow, it's usually preferable to preallocate a sufficient amount of memory using InitSiftData(), in particular if SIFT features are extracted from a continuous stream of video camera images. On repeated calls ExtractSift() will reuse memory previously allocated.

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<cudaImage.h>#include<cudaSift.h>/* Reserve memory space for a whole bunch of SIFT features. */SiftDatasiftData;
InitSiftData(siftData, 25000, true, true);
/* Read image using OpenCV and convert to floating point. */cv::Matlimg;
cv::imread("image.png", 0).convertTo(limg, CV32FC1);
/* Allocate 1280x960 pixel image with device side pitch of 1280 floats. *//* Memory on host side already allocated by OpenCV is reused. */CudaImageimg;
img.Allocate(1280, 960, 1280, false, NULL, (float*) limg.data);
/* Download image from host to device */img.Download();
intnumOctaves=5; /* Number of octaves in Gaussian pyramid */floatinitBlur=1.0f; /* Amount of initial Gaussian blurring in standard deviations */floatthresh=3.5f; /* Threshold on difference of Gaussians for feature pruning */floatminScale=0.0f; /* Minimum acceptable scale to remove fine-scale features */boolupScale= false; /* Whether to upscale image before extraction *//* Extract SIFT features */ExtractSift(siftData, img, numOctaves, initBlur, thresh, minScale, upScale);
...
/* Free space allocated from SIFT features */FreeSiftData(siftData);

Parameter setting

The requirements on number and quality of features vary from application to application. Some applications benefit from a smaller number of high quality features, while others require as many features as possible. More distinct features with higher DoG (difference of Gaussians) responses tend to be of higher quality and are easier to match between multiple views. With the parameter thresh a threshold can be set on the minimum DoG to prune features of less quality.

In many cases the most fine-scale features are of little use, especially when noise conditions are severe or when features are matched between very different views. In such cases the most fine-scale features can be pruned by setting minScale to the minimum acceptable feature scale, where 1.0 corresponds to the original image scale without upscaling. As a consequence of pruning the computational cost can also be reduced.

To increase the number of SIFT features, but also increase the computational cost, the original image can be automatically upscaled to double the size using the upScale parameter, in accordance to Lowe's recommendations. One should keep in mind though that by doing so the fraction of features that can be matched tend to go down, even if the total number of extracted features increases significantly. If it's enough to instead reduce the thresh parameter to get more features, that is often a better alternative.

Results without upscaling (upScale=False) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
1.0423640.4%5.8
1.5349142.5%5.2
2.0272043.2%4.7
2.5212144.4%4.2
3.0162745.8%3.9
3.5118946.2%3.6
4.088148.5%3.3

Results with upscaling (upScale=True) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
2.0450234.9%13.2
2.5338935.9%11.2
3.0252937.1%10.6
3.5184138.3%9.9
4.0133139.8%9.5
4.595442.2%9.3
5.061139.3%9.1

About

A CUDA implementation of SIFT for NVidia GPUs (1.2 ms on a GTX 1060)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CudaSift - SIFT features with CUDA

This is the fourth version of a SIFT (Scale Invariant Feature Transform) implementation using CUDA for GPUs from NVidia. The first version is from 2007 and GPUs have evolved since then. This version is slightly more precise and considerably faster than the previous versions and has been optimized for Kepler and later generations of GPUs.

On a GTX 1060 GPU the code takes about 1.2 ms on a 1280x960 pixel image and 1.7 ms on a 1920x1080 pixel image. There is also code for brute-force matching of features that takes about 2.2 ms for two sets of around 1900 SIFT features each.

The code relies on CMake for compilation and OpenCV for image containers. OpenCV can however be quite easily changed to something else. The code can be relatively hard to read, given the way things have been parallelized for maximum speed.

The code is free to use for non-commercial applications. If you use the code for research, please cite to the following paper.

M. Björkman, N. Bergström and D. Kragic, "Detecting, segmenting and tracking unknown objects using multi-label MRF inference", CVIU, 118, pp. 111-127, January 2014. ScienceDirect

Update in feature matching (2019-05-17)

The brute force feature matcher has been significantly improved in speed. The largest improvements can be seen for large feature sets with 10000 features or more, but as can be seen below, it performs rather well even with just 2000 features. The file match.pdf includes a description of the optimizations done in this version.

New version for Pascal (2018-10-26)

There is a new version optimized for Pascal cards, but it should work also on many older cards. Since it includes some bug fixes that changes slightly how features are extracted, which might affect matching to features extracted using an older version, the changes are kept in a new branch (Pascal). The fixes include a small change in ScaleDown that corrects an odd behaviour for images with heights not divisible by 2^(#octaves). The second change is a correction of an improper shift of (0.5,0.5) pixels, when pixel values were read from the image to create a descriptor.

Then there are some improvements in terms of speed, especially in the Laplace function, that detects DoG features, and the LowPass function, that is seen as preprocessing and is not included in the benchmarking below. Maybe surprisingly, even if optimizations were done with respect to Pascal cards, these improvements were even better for older cards. The changes involve trying to make each CUDA thread have more work to do, using fewer thread blocks. For typical images of today, there will be enough blocks to feed the streaming multiprocessors anyway.

Latest result of version under test:

1280x9601920x1080GFLOPSBandwidthMatching
TuringGeForce RTX 2080 Ti0.42*0.56*117506160.30*
PascalGeForce GTX 1080 Ti0.58*0.80*106094840.42*
PascalGeForce GTX 10601.21.738551922.2
MaxwellGeForce GTX 9701.31.834942242.5
KeplerTesla K40c2.43.442912884.7

Matching is done between two sets of 1911 and 2086 features respectively. A star indicates results from the last checked in version.

Benchmarking of new version (2018-08-22)

About every 2nd year, I try to update the code to gain even more speed through further optimization. Here are some results for a new version of the code. Improvements in speed have primarilly been gained by reducing communication between host and device, better balancing the load on caches, shared and global memory, and increasing the workload of each thread block.

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti0.71.0106094841.0
PascalGeForce GTX 10601.62.438551922.2
MaxwellGeForce GTX 9701.92.834942242.5
KeplerTesla K40c3.14.742912884.7
KeplerGeForce GTX TITAN2.94.345002884.5

Matching is done between two sets of 1818 and 1978 features respectively.

It's questionable whether further optimization really makes sense, given that the cost of just transfering an 1920x1080 pixel image to the device takes about 1.4 ms on a GTX 1080 Ti. Even if the brute force feature matcher is not much faster than earlier versions, it does not have the same O(N^2) temporary memory overhead, which is preferable if there are many features.

Benchmarking of previous version (2017-05-24)

Computational cost (in milliseconds) on different GPUs:

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti1.72.3106094841.4
PascalGeForce GTX 10602.74.038551922.6
MaxwellGeForce GTX 9703.85.634942242.8
KeplerTesla K40c5.48.042912885.5
KeplerGeForce GTX TITAN4.46.645002884.6

Matching is done between two sets of 1616 and 1769 features respectively.

The improvements in this version involved a slight adaptation for Pascal, changing from textures to global memory (mostly through L2) in the most costly function LaplaceMulti. The medium-end card GTX 1060 is impressive indeed.

Usage

There are two different containers for storing data on the host and on the device; SiftData for SIFT features and CudaImage for images. Since memory allocation on GPUs is slow, it's usually preferable to preallocate a sufficient amount of memory using InitSiftData(), in particular if SIFT features are extracted from a continuous stream of video camera images. On repeated calls ExtractSift() will reuse memory previously allocated.

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<cudaImage.h>#include<cudaSift.h>/* Reserve memory space for a whole bunch of SIFT features. */SiftDatasiftData;
InitSiftData(siftData, 25000, true, true);
/* Read image using OpenCV and convert to floating point. */cv::Matlimg;
cv::imread("image.png", 0).convertTo(limg, CV32FC1);
/* Allocate 1280x960 pixel image with device side pitch of 1280 floats. *//* Memory on host side already allocated by OpenCV is reused. */CudaImageimg;
img.Allocate(1280, 960, 1280, false, NULL, (float*) limg.data);
/* Download image from host to device */img.Download();
intnumOctaves=5; /* Number of octaves in Gaussian pyramid */floatinitBlur=1.0f; /* Amount of initial Gaussian blurring in standard deviations */floatthresh=3.5f; /* Threshold on difference of Gaussians for feature pruning */floatminScale=0.0f; /* Minimum acceptable scale to remove fine-scale features */boolupScale= false; /* Whether to upscale image before extraction *//* Extract SIFT features */ExtractSift(siftData, img, numOctaves, initBlur, thresh, minScale, upScale);
...
/* Free space allocated from SIFT features */FreeSiftData(siftData);

Parameter setting

The requirements on number and quality of features vary from application to application. Some applications benefit from a smaller number of high quality features, while others require as many features as possible. More distinct features with higher DoG (difference of Gaussians) responses tend to be of higher quality and are easier to match between multiple views. With the parameter thresh a threshold can be set on the minimum DoG to prune features of less quality.

In many cases the most fine-scale features are of little use, especially when noise conditions are severe or when features are matched between very different views. In such cases the most fine-scale features can be pruned by setting minScale to the minimum acceptable feature scale, where 1.0 corresponds to the original image scale without upscaling. As a consequence of pruning the computational cost can also be reduced.

To increase the number of SIFT features, but also increase the computational cost, the original image can be automatically upscaled to double the size using the upScale parameter, in accordance to Lowe's recommendations. One should keep in mind though that by doing so the fraction of features that can be matched tend to go down, even if the total number of extracted features increases significantly. If it's enough to instead reduce the thresh parameter to get more features, that is often a better alternative.

Results without upscaling (upScale=False) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
1.0423640.4%5.8
1.5349142.5%5.2
2.0272043.2%4.7
2.5212144.4%4.2
3.0162745.8%3.9
3.5118946.2%3.6
4.088148.5%3.3

Results with upscaling (upScale=True) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
2.0450234.9%13.2
2.5338935.9%11.2
3.0252937.1%10.6
3.5184138.3%9.9
4.0133139.8%9.5
4.595442.2%9.3
5.061139.3%9.1

About

A CUDA implementation of SIFT for NVidia GPUs (1.2 ms on a GTX 1060)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CudaSift - SIFT features with CUDA

This is the fourth version of a SIFT (Scale Invariant Feature Transform) implementation using CUDA for GPUs from NVidia. The first version is from 2007 and GPUs have evolved since then. This version is slightly more precise and considerably faster than the previous versions and has been optimized for Kepler and later generations of GPUs.

On a GTX 1060 GPU the code takes about 1.2 ms on a 1280x960 pixel image and 1.7 ms on a 1920x1080 pixel image. There is also code for brute-force matching of features that takes about 2.2 ms for two sets of around 1900 SIFT features each.

The code relies on CMake for compilation and OpenCV for image containers. OpenCV can however be quite easily changed to something else. The code can be relatively hard to read, given the way things have been parallelized for maximum speed.

The code is free to use for non-commercial applications. If you use the code for research, please cite to the following paper.

M. Björkman, N. Bergström and D. Kragic, "Detecting, segmenting and tracking unknown objects using multi-label MRF inference", CVIU, 118, pp. 111-127, January 2014. ScienceDirect

Update in feature matching (2019-05-17)

The brute force feature matcher has been significantly improved in speed. The largest improvements can be seen for large feature sets with 10000 features or more, but as can be seen below, it performs rather well even with just 2000 features. The file match.pdf includes a description of the optimizations done in this version.

New version for Pascal (2018-10-26)

There is a new version optimized for Pascal cards, but it should work also on many older cards. Since it includes some bug fixes that changes slightly how features are extracted, which might affect matching to features extracted using an older version, the changes are kept in a new branch (Pascal). The fixes include a small change in ScaleDown that corrects an odd behaviour for images with heights not divisible by 2^(#octaves). The second change is a correction of an improper shift of (0.5,0.5) pixels, when pixel values were read from the image to create a descriptor.

Then there are some improvements in terms of speed, especially in the Laplace function, that detects DoG features, and the LowPass function, that is seen as preprocessing and is not included in the benchmarking below. Maybe surprisingly, even if optimizations were done with respect to Pascal cards, these improvements were even better for older cards. The changes involve trying to make each CUDA thread have more work to do, using fewer thread blocks. For typical images of today, there will be enough blocks to feed the streaming multiprocessors anyway.

Latest result of version under test:

1280x9601920x1080GFLOPSBandwidthMatching
TuringGeForce RTX 2080 Ti0.42*0.56*117506160.30*
PascalGeForce GTX 1080 Ti0.58*0.80*106094840.42*
PascalGeForce GTX 10601.21.738551922.2
MaxwellGeForce GTX 9701.31.834942242.5
KeplerTesla K40c2.43.442912884.7

Matching is done between two sets of 1911 and 2086 features respectively. A star indicates results from the last checked in version.

Benchmarking of new version (2018-08-22)

About every 2nd year, I try to update the code to gain even more speed through further optimization. Here are some results for a new version of the code. Improvements in speed have primarilly been gained by reducing communication between host and device, better balancing the load on caches, shared and global memory, and increasing the workload of each thread block.

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti0.71.0106094841.0
PascalGeForce GTX 10601.62.438551922.2
MaxwellGeForce GTX 9701.92.834942242.5
KeplerTesla K40c3.14.742912884.7
KeplerGeForce GTX TITAN2.94.345002884.5

Matching is done between two sets of 1818 and 1978 features respectively.

It's questionable whether further optimization really makes sense, given that the cost of just transfering an 1920x1080 pixel image to the device takes about 1.4 ms on a GTX 1080 Ti. Even if the brute force feature matcher is not much faster than earlier versions, it does not have the same O(N^2) temporary memory overhead, which is preferable if there are many features.

Benchmarking of previous version (2017-05-24)

Computational cost (in milliseconds) on different GPUs:

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti1.72.3106094841.4
PascalGeForce GTX 10602.74.038551922.6
MaxwellGeForce GTX 9703.85.634942242.8
KeplerTesla K40c5.48.042912885.5
KeplerGeForce GTX TITAN4.46.645002884.6

Matching is done between two sets of 1616 and 1769 features respectively.

The improvements in this version involved a slight adaptation for Pascal, changing from textures to global memory (mostly through L2) in the most costly function LaplaceMulti. The medium-end card GTX 1060 is impressive indeed.

Usage

There are two different containers for storing data on the host and on the device; SiftData for SIFT features and CudaImage for images. Since memory allocation on GPUs is slow, it's usually preferable to preallocate a sufficient amount of memory using InitSiftData(), in particular if SIFT features are extracted from a continuous stream of video camera images. On repeated calls ExtractSift() will reuse memory previously allocated.

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<cudaImage.h>#include<cudaSift.h>/* Reserve memory space for a whole bunch of SIFT features. */SiftDatasiftData;
InitSiftData(siftData, 25000, true, true);
/* Read image using OpenCV and convert to floating point. */cv::Matlimg;
cv::imread("image.png", 0).convertTo(limg, CV32FC1);
/* Allocate 1280x960 pixel image with device side pitch of 1280 floats. *//* Memory on host side already allocated by OpenCV is reused. */CudaImageimg;
img.Allocate(1280, 960, 1280, false, NULL, (float*) limg.data);
/* Download image from host to device */img.Download();
intnumOctaves=5; /* Number of octaves in Gaussian pyramid */floatinitBlur=1.0f; /* Amount of initial Gaussian blurring in standard deviations */floatthresh=3.5f; /* Threshold on difference of Gaussians for feature pruning */floatminScale=0.0f; /* Minimum acceptable scale to remove fine-scale features */boolupScale= false; /* Whether to upscale image before extraction *//* Extract SIFT features */ExtractSift(siftData, img, numOctaves, initBlur, thresh, minScale, upScale);
...
/* Free space allocated from SIFT features */FreeSiftData(siftData);

Parameter setting

The requirements on number and quality of features vary from application to application. Some applications benefit from a smaller number of high quality features, while others require as many features as possible. More distinct features with higher DoG (difference of Gaussians) responses tend to be of higher quality and are easier to match between multiple views. With the parameter thresh a threshold can be set on the minimum DoG to prune features of less quality.

In many cases the most fine-scale features are of little use, especially when noise conditions are severe or when features are matched between very different views. In such cases the most fine-scale features can be pruned by setting minScale to the minimum acceptable feature scale, where 1.0 corresponds to the original image scale without upscaling. As a consequence of pruning the computational cost can also be reduced.

To increase the number of SIFT features, but also increase the computational cost, the original image can be automatically upscaled to double the size using the upScale parameter, in accordance to Lowe's recommendations. One should keep in mind though that by doing so the fraction of features that can be matched tend to go down, even if the total number of extracted features increases significantly. If it's enough to instead reduce the thresh parameter to get more features, that is often a better alternative.

Results without upscaling (upScale=False) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
1.0423640.4%5.8
1.5349142.5%5.2
2.0272043.2%4.7
2.5212144.4%4.2
3.0162745.8%3.9
3.5118946.2%3.6
4.088148.5%3.3

Results with upscaling (upScale=True) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
2.0450234.9%13.2
2.5338935.9%11.2
3.0252937.1%10.6
3.5184138.3%9.9
4.0133139.8%9.5
4.595442.2%9.3
5.061139.3%9.1

About

A CUDA implementation of SIFT for NVidia GPUs (1.2 ms on a GTX 1060)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

CudaSift - SIFT features with CUDA

This is the fourth version of a SIFT (Scale Invariant Feature Transform) implementation using CUDA for GPUs from NVidia. The first version is from 2007 and GPUs have evolved since then. This version is slightly more precise and considerably faster than the previous versions and has been optimized for Kepler and later generations of GPUs.

On a GTX 1060 GPU the code takes about 1.2 ms on a 1280x960 pixel image and 1.7 ms on a 1920x1080 pixel image. There is also code for brute-force matching of features that takes about 2.2 ms for two sets of around 1900 SIFT features each.

The code relies on CMake for compilation and OpenCV for image containers. OpenCV can however be quite easily changed to something else. The code can be relatively hard to read, given the way things have been parallelized for maximum speed.

The code is free to use for non-commercial applications. If you use the code for research, please cite to the following paper.

M. Björkman, N. Bergström and D. Kragic, "Detecting, segmenting and tracking unknown objects using multi-label MRF inference", CVIU, 118, pp. 111-127, January 2014. ScienceDirect

Update in feature matching (2019-05-17)

The brute force feature matcher has been significantly improved in speed. The largest improvements can be seen for large feature sets with 10000 features or more, but as can be seen below, it performs rather well even with just 2000 features. The file match.pdf includes a description of the optimizations done in this version.

New version for Pascal (2018-10-26)

There is a new version optimized for Pascal cards, but it should work also on many older cards. Since it includes some bug fixes that changes slightly how features are extracted, which might affect matching to features extracted using an older version, the changes are kept in a new branch (Pascal). The fixes include a small change in ScaleDown that corrects an odd behaviour for images with heights not divisible by 2^(#octaves). The second change is a correction of an improper shift of (0.5,0.5) pixels, when pixel values were read from the image to create a descriptor.

Then there are some improvements in terms of speed, especially in the Laplace function, that detects DoG features, and the LowPass function, that is seen as preprocessing and is not included in the benchmarking below. Maybe surprisingly, even if optimizations were done with respect to Pascal cards, these improvements were even better for older cards. The changes involve trying to make each CUDA thread have more work to do, using fewer thread blocks. For typical images of today, there will be enough blocks to feed the streaming multiprocessors anyway.

Latest result of version under test:

1280x9601920x1080GFLOPSBandwidthMatching
TuringGeForce RTX 2080 Ti0.42*0.56*117506160.30*
PascalGeForce GTX 1080 Ti0.58*0.80*106094840.42*
PascalGeForce GTX 10601.21.738551922.2
MaxwellGeForce GTX 9701.31.834942242.5
KeplerTesla K40c2.43.442912884.7

Matching is done between two sets of 1911 and 2086 features respectively. A star indicates results from the last checked in version.

Benchmarking of new version (2018-08-22)

About every 2nd year, I try to update the code to gain even more speed through further optimization. Here are some results for a new version of the code. Improvements in speed have primarilly been gained by reducing communication between host and device, better balancing the load on caches, shared and global memory, and increasing the workload of each thread block.

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti0.71.0106094841.0
PascalGeForce GTX 10601.62.438551922.2
MaxwellGeForce GTX 9701.92.834942242.5
KeplerTesla K40c3.14.742912884.7
KeplerGeForce GTX TITAN2.94.345002884.5

Matching is done between two sets of 1818 and 1978 features respectively.

It's questionable whether further optimization really makes sense, given that the cost of just transfering an 1920x1080 pixel image to the device takes about 1.4 ms on a GTX 1080 Ti. Even if the brute force feature matcher is not much faster than earlier versions, it does not have the same O(N^2) temporary memory overhead, which is preferable if there are many features.

Benchmarking of previous version (2017-05-24)

Computational cost (in milliseconds) on different GPUs:

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti1.72.3106094841.4
PascalGeForce GTX 10602.74.038551922.6
MaxwellGeForce GTX 9703.85.634942242.8
KeplerTesla K40c5.48.042912885.5
KeplerGeForce GTX TITAN4.46.645002884.6

Matching is done between two sets of 1616 and 1769 features respectively.

The improvements in this version involved a slight adaptation for Pascal, changing from textures to global memory (mostly through L2) in the most costly function LaplaceMulti. The medium-end card GTX 1060 is impressive indeed.

Usage

There are two different containers for storing data on the host and on the device; SiftData for SIFT features and CudaImage for images. Since memory allocation on GPUs is slow, it's usually preferable to preallocate a sufficient amount of memory using InitSiftData(), in particular if SIFT features are extracted from a continuous stream of video camera images. On repeated calls ExtractSift() will reuse memory previously allocated.

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<cudaImage.h>#include<cudaSift.h>/* Reserve memory space for a whole bunch of SIFT features. */SiftDatasiftData;
InitSiftData(siftData, 25000, true, true);
/* Read image using OpenCV and convert to floating point. */cv::Matlimg;
cv::imread("image.png", 0).convertTo(limg, CV32FC1);
/* Allocate 1280x960 pixel image with device side pitch of 1280 floats. *//* Memory on host side already allocated by OpenCV is reused. */CudaImageimg;
img.Allocate(1280, 960, 1280, false, NULL, (float*) limg.data);
/* Download image from host to device */img.Download();
intnumOctaves=5; /* Number of octaves in Gaussian pyramid */floatinitBlur=1.0f; /* Amount of initial Gaussian blurring in standard deviations */floatthresh=3.5f; /* Threshold on difference of Gaussians for feature pruning */floatminScale=0.0f; /* Minimum acceptable scale to remove fine-scale features */boolupScale= false; /* Whether to upscale image before extraction *//* Extract SIFT features */ExtractSift(siftData, img, numOctaves, initBlur, thresh, minScale, upScale);
...
/* Free space allocated from SIFT features */FreeSiftData(siftData);

Parameter setting

The requirements on number and quality of features vary from application to application. Some applications benefit from a smaller number of high quality features, while others require as many features as possible. More distinct features with higher DoG (difference of Gaussians) responses tend to be of higher quality and are easier to match between multiple views. With the parameter thresh a threshold can be set on the minimum DoG to prune features of less quality.

In many cases the most fine-scale features are of little use, especially when noise conditions are severe or when features are matched between very different views. In such cases the most fine-scale features can be pruned by setting minScale to the minimum acceptable feature scale, where 1.0 corresponds to the original image scale without upscaling. As a consequence of pruning the computational cost can also be reduced.

To increase the number of SIFT features, but also increase the computational cost, the original image can be automatically upscaled to double the size using the upScale parameter, in accordance to Lowe's recommendations. One should keep in mind though that by doing so the fraction of features that can be matched tend to go down, even if the total number of extracted features increases significantly. If it's enough to instead reduce the thresh parameter to get more features, that is often a better alternative.

Results without upscaling (upScale=False) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
1.0423640.4%5.8
1.5349142.5%5.2
2.0272043.2%4.7
2.5212144.4%4.2
3.0162745.8%3.9
3.5118946.2%3.6
4.088148.5%3.3

Results with upscaling (upScale=True) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
2.0450234.9%13.2
2.5338935.9%11.2
3.0252937.1%10.6
3.5184138.3%9.9
4.0133139.8%9.5
4.595442.2%9.3
5.061139.3%9.1

About

A CUDA implementation of SIFT for NVidia GPUs (1.2 ms on a GTX 1060)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CudaSift - SIFT features with CUDA

This is the fourth version of a SIFT (Scale Invariant Feature Transform) implementation using CUDA for GPUs from NVidia. The first version is from 2007 and GPUs have evolved since then. This version is slightly more precise and considerably faster than the previous versions and has been optimized for Kepler and later generations of GPUs.

On a GTX 1060 GPU the code takes about 1.2 ms on a 1280x960 pixel image and 1.7 ms on a 1920x1080 pixel image. There is also code for brute-force matching of features that takes about 2.2 ms for two sets of around 1900 SIFT features each.

The code relies on CMake for compilation and OpenCV for image containers. OpenCV can however be quite easily changed to something else. The code can be relatively hard to read, given the way things have been parallelized for maximum speed.

The code is free to use for non-commercial applications. If you use the code for research, please cite to the following paper.

M. Björkman, N. Bergström and D. Kragic, "Detecting, segmenting and tracking unknown objects using multi-label MRF inference", CVIU, 118, pp. 111-127, January 2014. ScienceDirect

Update in feature matching (2019-05-17)

The brute force feature matcher has been significantly improved in speed. The largest improvements can be seen for large feature sets with 10000 features or more, but as can be seen below, it performs rather well even with just 2000 features. The file match.pdf includes a description of the optimizations done in this version.

New version for Pascal (2018-10-26)

There is a new version optimized for Pascal cards, but it should work also on many older cards. Since it includes some bug fixes that changes slightly how features are extracted, which might affect matching to features extracted using an older version, the changes are kept in a new branch (Pascal). The fixes include a small change in ScaleDown that corrects an odd behaviour for images with heights not divisible by 2^(#octaves). The second change is a correction of an improper shift of (0.5,0.5) pixels, when pixel values were read from the image to create a descriptor.

Then there are some improvements in terms of speed, especially in the Laplace function, that detects DoG features, and the LowPass function, that is seen as preprocessing and is not included in the benchmarking below. Maybe surprisingly, even if optimizations were done with respect to Pascal cards, these improvements were even better for older cards. The changes involve trying to make each CUDA thread have more work to do, using fewer thread blocks. For typical images of today, there will be enough blocks to feed the streaming multiprocessors anyway.

Latest result of version under test:

1280x9601920x1080GFLOPSBandwidthMatching
TuringGeForce RTX 2080 Ti0.42*0.56*117506160.30*
PascalGeForce GTX 1080 Ti0.58*0.80*106094840.42*
PascalGeForce GTX 10601.21.738551922.2
MaxwellGeForce GTX 9701.31.834942242.5
KeplerTesla K40c2.43.442912884.7

Matching is done between two sets of 1911 and 2086 features respectively. A star indicates results from the last checked in version.

Benchmarking of new version (2018-08-22)

About every 2nd year, I try to update the code to gain even more speed through further optimization. Here are some results for a new version of the code. Improvements in speed have primarilly been gained by reducing communication between host and device, better balancing the load on caches, shared and global memory, and increasing the workload of each thread block.

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti0.71.0106094841.0
PascalGeForce GTX 10601.62.438551922.2
MaxwellGeForce GTX 9701.92.834942242.5
KeplerTesla K40c3.14.742912884.7
KeplerGeForce GTX TITAN2.94.345002884.5

Matching is done between two sets of 1818 and 1978 features respectively.

It's questionable whether further optimization really makes sense, given that the cost of just transfering an 1920x1080 pixel image to the device takes about 1.4 ms on a GTX 1080 Ti. Even if the brute force feature matcher is not much faster than earlier versions, it does not have the same O(N^2) temporary memory overhead, which is preferable if there are many features.

Benchmarking of previous version (2017-05-24)

Computational cost (in milliseconds) on different GPUs:

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti1.72.3106094841.4
PascalGeForce GTX 10602.74.038551922.6
MaxwellGeForce GTX 9703.85.634942242.8
KeplerTesla K40c5.48.042912885.5
KeplerGeForce GTX TITAN4.46.645002884.6

Matching is done between two sets of 1616 and 1769 features respectively.

The improvements in this version involved a slight adaptation for Pascal, changing from textures to global memory (mostly through L2) in the most costly function LaplaceMulti. The medium-end card GTX 1060 is impressive indeed.

Usage

There are two different containers for storing data on the host and on the device; SiftData for SIFT features and CudaImage for images. Since memory allocation on GPUs is slow, it's usually preferable to preallocate a sufficient amount of memory using InitSiftData(), in particular if SIFT features are extracted from a continuous stream of video camera images. On repeated calls ExtractSift() will reuse memory previously allocated.

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<cudaImage.h>#include<cudaSift.h>/* Reserve memory space for a whole bunch of SIFT features. */SiftDatasiftData;
InitSiftData(siftData, 25000, true, true);
/* Read image using OpenCV and convert to floating point. */cv::Matlimg;
cv::imread("image.png", 0).convertTo(limg, CV32FC1);
/* Allocate 1280x960 pixel image with device side pitch of 1280 floats. *//* Memory on host side already allocated by OpenCV is reused. */CudaImageimg;
img.Allocate(1280, 960, 1280, false, NULL, (float*) limg.data);
/* Download image from host to device */img.Download();
intnumOctaves=5; /* Number of octaves in Gaussian pyramid */floatinitBlur=1.0f; /* Amount of initial Gaussian blurring in standard deviations */floatthresh=3.5f; /* Threshold on difference of Gaussians for feature pruning */floatminScale=0.0f; /* Minimum acceptable scale to remove fine-scale features */boolupScale= false; /* Whether to upscale image before extraction *//* Extract SIFT features */ExtractSift(siftData, img, numOctaves, initBlur, thresh, minScale, upScale);
...
/* Free space allocated from SIFT features */FreeSiftData(siftData);

Parameter setting

The requirements on number and quality of features vary from application to application. Some applications benefit from a smaller number of high quality features, while others require as many features as possible. More distinct features with higher DoG (difference of Gaussians) responses tend to be of higher quality and are easier to match between multiple views. With the parameter thresh a threshold can be set on the minimum DoG to prune features of less quality.

In many cases the most fine-scale features are of little use, especially when noise conditions are severe or when features are matched between very different views. In such cases the most fine-scale features can be pruned by setting minScale to the minimum acceptable feature scale, where 1.0 corresponds to the original image scale without upscaling. As a consequence of pruning the computational cost can also be reduced.

To increase the number of SIFT features, but also increase the computational cost, the original image can be automatically upscaled to double the size using the upScale parameter, in accordance to Lowe's recommendations. One should keep in mind though that by doing so the fraction of features that can be matched tend to go down, even if the total number of extracted features increases significantly. If it's enough to instead reduce the thresh parameter to get more features, that is often a better alternative.

Results without upscaling (upScale=False) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
1.0423640.4%5.8
1.5349142.5%5.2
2.0272043.2%4.7
2.5212144.4%4.2
3.0162745.8%3.9
3.5118946.2%3.6
4.088148.5%3.3

Results with upscaling (upScale=True) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
2.0450234.9%13.2
2.5338935.9%11.2
3.0252937.1%10.6
3.5184138.3%9.9
4.0133139.8%9.5
4.595442.2%9.3
5.061139.3%9.1

About

A CUDA implementation of SIFT for NVidia GPUs (1.2 ms on a GTX 1060)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CudaSift - SIFT features with CUDA

This is the fourth version of a SIFT (Scale Invariant Feature Transform) implementation using CUDA for GPUs from NVidia. The first version is from 2007 and GPUs have evolved since then. This version is slightly more precise and considerably faster than the previous versions and has been optimized for Kepler and later generations of GPUs.

On a GTX 1060 GPU the code takes about 1.2 ms on a 1280x960 pixel image and 1.7 ms on a 1920x1080 pixel image. There is also code for brute-force matching of features that takes about 2.2 ms for two sets of around 1900 SIFT features each.

The code relies on CMake for compilation and OpenCV for image containers. OpenCV can however be quite easily changed to something else. The code can be relatively hard to read, given the way things have been parallelized for maximum speed.

The code is free to use for non-commercial applications. If you use the code for research, please cite to the following paper.

M. Björkman, N. Bergström and D. Kragic, "Detecting, segmenting and tracking unknown objects using multi-label MRF inference", CVIU, 118, pp. 111-127, January 2014. ScienceDirect

Update in feature matching (2019-05-17)

The brute force feature matcher has been significantly improved in speed. The largest improvements can be seen for large feature sets with 10000 features or more, but as can be seen below, it performs rather well even with just 2000 features. The file match.pdf includes a description of the optimizations done in this version.

New version for Pascal (2018-10-26)

There is a new version optimized for Pascal cards, but it should work also on many older cards. Since it includes some bug fixes that changes slightly how features are extracted, which might affect matching to features extracted using an older version, the changes are kept in a new branch (Pascal). The fixes include a small change in ScaleDown that corrects an odd behaviour for images with heights not divisible by 2^(#octaves). The second change is a correction of an improper shift of (0.5,0.5) pixels, when pixel values were read from the image to create a descriptor.

Then there are some improvements in terms of speed, especially in the Laplace function, that detects DoG features, and the LowPass function, that is seen as preprocessing and is not included in the benchmarking below. Maybe surprisingly, even if optimizations were done with respect to Pascal cards, these improvements were even better for older cards. The changes involve trying to make each CUDA thread have more work to do, using fewer thread blocks. For typical images of today, there will be enough blocks to feed the streaming multiprocessors anyway.

Latest result of version under test:

1280x9601920x1080GFLOPSBandwidthMatching
TuringGeForce RTX 2080 Ti0.42*0.56*117506160.30*
PascalGeForce GTX 1080 Ti0.58*0.80*106094840.42*
PascalGeForce GTX 10601.21.738551922.2
MaxwellGeForce GTX 9701.31.834942242.5
KeplerTesla K40c2.43.442912884.7

Matching is done between two sets of 1911 and 2086 features respectively. A star indicates results from the last checked in version.

Benchmarking of new version (2018-08-22)

About every 2nd year, I try to update the code to gain even more speed through further optimization. Here are some results for a new version of the code. Improvements in speed have primarilly been gained by reducing communication between host and device, better balancing the load on caches, shared and global memory, and increasing the workload of each thread block.

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti0.71.0106094841.0
PascalGeForce GTX 10601.62.438551922.2
MaxwellGeForce GTX 9701.92.834942242.5
KeplerTesla K40c3.14.742912884.7
KeplerGeForce GTX TITAN2.94.345002884.5

Matching is done between two sets of 1818 and 1978 features respectively.

It's questionable whether further optimization really makes sense, given that the cost of just transfering an 1920x1080 pixel image to the device takes about 1.4 ms on a GTX 1080 Ti. Even if the brute force feature matcher is not much faster than earlier versions, it does not have the same O(N^2) temporary memory overhead, which is preferable if there are many features.

Benchmarking of previous version (2017-05-24)

Computational cost (in milliseconds) on different GPUs:

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti1.72.3106094841.4
PascalGeForce GTX 10602.74.038551922.6
MaxwellGeForce GTX 9703.85.634942242.8
KeplerTesla K40c5.48.042912885.5
KeplerGeForce GTX TITAN4.46.645002884.6

Matching is done between two sets of 1616 and 1769 features respectively.

The improvements in this version involved a slight adaptation for Pascal, changing from textures to global memory (mostly through L2) in the most costly function LaplaceMulti. The medium-end card GTX 1060 is impressive indeed.

Usage

There are two different containers for storing data on the host and on the device; SiftData for SIFT features and CudaImage for images. Since memory allocation on GPUs is slow, it's usually preferable to preallocate a sufficient amount of memory using InitSiftData(), in particular if SIFT features are extracted from a continuous stream of video camera images. On repeated calls ExtractSift() will reuse memory previously allocated.

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<cudaImage.h>#include<cudaSift.h>/* Reserve memory space for a whole bunch of SIFT features. */SiftDatasiftData;
InitSiftData(siftData, 25000, true, true);
/* Read image using OpenCV and convert to floating point. */cv::Matlimg;
cv::imread("image.png", 0).convertTo(limg, CV32FC1);
/* Allocate 1280x960 pixel image with device side pitch of 1280 floats. *//* Memory on host side already allocated by OpenCV is reused. */CudaImageimg;
img.Allocate(1280, 960, 1280, false, NULL, (float*) limg.data);
/* Download image from host to device */img.Download();
intnumOctaves=5; /* Number of octaves in Gaussian pyramid */floatinitBlur=1.0f; /* Amount of initial Gaussian blurring in standard deviations */floatthresh=3.5f; /* Threshold on difference of Gaussians for feature pruning */floatminScale=0.0f; /* Minimum acceptable scale to remove fine-scale features */boolupScale= false; /* Whether to upscale image before extraction *//* Extract SIFT features */ExtractSift(siftData, img, numOctaves, initBlur, thresh, minScale, upScale);
...
/* Free space allocated from SIFT features */FreeSiftData(siftData);

Parameter setting

The requirements on number and quality of features vary from application to application. Some applications benefit from a smaller number of high quality features, while others require as many features as possible. More distinct features with higher DoG (difference of Gaussians) responses tend to be of higher quality and are easier to match between multiple views. With the parameter thresh a threshold can be set on the minimum DoG to prune features of less quality.

In many cases the most fine-scale features are of little use, especially when noise conditions are severe or when features are matched between very different views. In such cases the most fine-scale features can be pruned by setting minScale to the minimum acceptable feature scale, where 1.0 corresponds to the original image scale without upscaling. As a consequence of pruning the computational cost can also be reduced.

To increase the number of SIFT features, but also increase the computational cost, the original image can be automatically upscaled to double the size using the upScale parameter, in accordance to Lowe's recommendations. One should keep in mind though that by doing so the fraction of features that can be matched tend to go down, even if the total number of extracted features increases significantly. If it's enough to instead reduce the thresh parameter to get more features, that is often a better alternative.

Results without upscaling (upScale=False) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
1.0423640.4%5.8
1.5349142.5%5.2
2.0272043.2%4.7
2.5212144.4%4.2
3.0162745.8%3.9
3.5118946.2%3.6
4.088148.5%3.3

Results with upscaling (upScale=True) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
2.0450234.9%13.2
2.5338935.9%11.2
3.0252937.1%10.6
3.5184138.3%9.9
4.0133139.8%9.5
4.595442.2%9.3
5.061139.3%9.1

About

A CUDA implementation of SIFT for NVidia GPUs (1.2 ms on a GTX 1060)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

CudaSift - SIFT features with CUDA

This is the fourth version of a SIFT (Scale Invariant Feature Transform) implementation using CUDA for GPUs from NVidia. The first version is from 2007 and GPUs have evolved since then. This version is slightly more precise and considerably faster than the previous versions and has been optimized for Kepler and later generations of GPUs.

On a GTX 1060 GPU the code takes about 1.2 ms on a 1280x960 pixel image and 1.7 ms on a 1920x1080 pixel image. There is also code for brute-force matching of features that takes about 2.2 ms for two sets of around 1900 SIFT features each.

The code relies on CMake for compilation and OpenCV for image containers. OpenCV can however be quite easily changed to something else. The code can be relatively hard to read, given the way things have been parallelized for maximum speed.

The code is free to use for non-commercial applications. If you use the code for research, please cite to the following paper.

M. Björkman, N. Bergström and D. Kragic, "Detecting, segmenting and tracking unknown objects using multi-label MRF inference", CVIU, 118, pp. 111-127, January 2014. ScienceDirect

Update in feature matching (2019-05-17)

The brute force feature matcher has been significantly improved in speed. The largest improvements can be seen for large feature sets with 10000 features or more, but as can be seen below, it performs rather well even with just 2000 features. The file match.pdf includes a description of the optimizations done in this version.

New version for Pascal (2018-10-26)

There is a new version optimized for Pascal cards, but it should work also on many older cards. Since it includes some bug fixes that changes slightly how features are extracted, which might affect matching to features extracted using an older version, the changes are kept in a new branch (Pascal). The fixes include a small change in ScaleDown that corrects an odd behaviour for images with heights not divisible by 2^(#octaves). The second change is a correction of an improper shift of (0.5,0.5) pixels, when pixel values were read from the image to create a descriptor.

Then there are some improvements in terms of speed, especially in the Laplace function, that detects DoG features, and the LowPass function, that is seen as preprocessing and is not included in the benchmarking below. Maybe surprisingly, even if optimizations were done with respect to Pascal cards, these improvements were even better for older cards. The changes involve trying to make each CUDA thread have more work to do, using fewer thread blocks. For typical images of today, there will be enough blocks to feed the streaming multiprocessors anyway.

Latest result of version under test:

1280x9601920x1080GFLOPSBandwidthMatching
TuringGeForce RTX 2080 Ti0.42*0.56*117506160.30*
PascalGeForce GTX 1080 Ti0.58*0.80*106094840.42*
PascalGeForce GTX 10601.21.738551922.2
MaxwellGeForce GTX 9701.31.834942242.5
KeplerTesla K40c2.43.442912884.7

Matching is done between two sets of 1911 and 2086 features respectively. A star indicates results from the last checked in version.

Benchmarking of new version (2018-08-22)

About every 2nd year, I try to update the code to gain even more speed through further optimization. Here are some results for a new version of the code. Improvements in speed have primarilly been gained by reducing communication between host and device, better balancing the load on caches, shared and global memory, and increasing the workload of each thread block.

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti0.71.0106094841.0
PascalGeForce GTX 10601.62.438551922.2
MaxwellGeForce GTX 9701.92.834942242.5
KeplerTesla K40c3.14.742912884.7
KeplerGeForce GTX TITAN2.94.345002884.5

Matching is done between two sets of 1818 and 1978 features respectively.

It's questionable whether further optimization really makes sense, given that the cost of just transfering an 1920x1080 pixel image to the device takes about 1.4 ms on a GTX 1080 Ti. Even if the brute force feature matcher is not much faster than earlier versions, it does not have the same O(N^2) temporary memory overhead, which is preferable if there are many features.

Benchmarking of previous version (2017-05-24)

Computational cost (in milliseconds) on different GPUs:

1280x9601920x1080GFLOPSBandwidthMatching
PascalGeForce GTX 1080 Ti1.72.3106094841.4
PascalGeForce GTX 10602.74.038551922.6
MaxwellGeForce GTX 9703.85.634942242.8
KeplerTesla K40c5.48.042912885.5
KeplerGeForce GTX TITAN4.46.645002884.6

Matching is done between two sets of 1616 and 1769 features respectively.

The improvements in this version involved a slight adaptation for Pascal, changing from textures to global memory (mostly through L2) in the most costly function LaplaceMulti. The medium-end card GTX 1060 is impressive indeed.

Usage

There are two different containers for storing data on the host and on the device; SiftData for SIFT features and CudaImage for images. Since memory allocation on GPUs is slow, it's usually preferable to preallocate a sufficient amount of memory using InitSiftData(), in particular if SIFT features are extracted from a continuous stream of video camera images. On repeated calls ExtractSift() will reuse memory previously allocated.

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<cudaImage.h>#include<cudaSift.h>/* Reserve memory space for a whole bunch of SIFT features. */SiftDatasiftData;
InitSiftData(siftData, 25000, true, true);
/* Read image using OpenCV and convert to floating point. */cv::Matlimg;
cv::imread("image.png", 0).convertTo(limg, CV32FC1);
/* Allocate 1280x960 pixel image with device side pitch of 1280 floats. *//* Memory on host side already allocated by OpenCV is reused. */CudaImageimg;
img.Allocate(1280, 960, 1280, false, NULL, (float*) limg.data);
/* Download image from host to device */img.Download();
intnumOctaves=5; /* Number of octaves in Gaussian pyramid */floatinitBlur=1.0f; /* Amount of initial Gaussian blurring in standard deviations */floatthresh=3.5f; /* Threshold on difference of Gaussians for feature pruning */floatminScale=0.0f; /* Minimum acceptable scale to remove fine-scale features */boolupScale= false; /* Whether to upscale image before extraction *//* Extract SIFT features */ExtractSift(siftData, img, numOctaves, initBlur, thresh, minScale, upScale);
...
/* Free space allocated from SIFT features */FreeSiftData(siftData);

Parameter setting

The requirements on number and quality of features vary from application to application. Some applications benefit from a smaller number of high quality features, while others require as many features as possible. More distinct features with higher DoG (difference of Gaussians) responses tend to be of higher quality and are easier to match between multiple views. With the parameter thresh a threshold can be set on the minimum DoG to prune features of less quality.

In many cases the most fine-scale features are of little use, especially when noise conditions are severe or when features are matched between very different views. In such cases the most fine-scale features can be pruned by setting minScale to the minimum acceptable feature scale, where 1.0 corresponds to the original image scale without upscaling. As a consequence of pruning the computational cost can also be reduced.

To increase the number of SIFT features, but also increase the computational cost, the original image can be automatically upscaled to double the size using the upScale parameter, in accordance to Lowe's recommendations. One should keep in mind though that by doing so the fraction of features that can be matched tend to go down, even if the total number of extracted features increases significantly. If it's enough to instead reduce the thresh parameter to get more features, that is often a better alternative.

Results without upscaling (upScale=False) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
1.0423640.4%5.8
1.5349142.5%5.2
2.0272043.2%4.7
2.5212144.4%4.2
3.0162745.8%3.9
3.5118946.2%3.6
4.088148.5%3.3

Results with upscaling (upScale=True) of 1280x960 pixel input image.

thresh#Matches%MatchesCost (ms)
2.0450234.9%13.2
2.5338935.9%11.2
3.0252937.1%10.6
3.5184138.3%9.9
4.0133139.8%9.5
4.595442.2%9.3
5.061139.3%9.1

About

A CUDA implementation of SIFT for NVidia GPUs (1.2 ms on a GTX 1060)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages