Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Dense DAISY feature description #384

Merged
merged 21 commits into from

4 participants

@andersbll

Hi there,

I propose to include the DAISY feature descriptor [1], which I recently reimplemented in Python following the paper and the original C++ code 2.

Thanks for a great library!
Anders

[1]: Tola, Engin, Vincent Lepetit, and Pascal Fua. "Daisy: An efficient dense descriptor applied to wide-baseline stereo." Pattern Analysis and Machine Intelligence, IEEE Transactions on 32.5 (2010): 815-830.

Anders Boese... added some commits
Anders Boesen Lindbo Larsen Add dense DAISY feature description. 2eb03c0
Anders Boesen Lindbo Larsen PEP8 style corrections. 979a15c
skimage/feature/_daisy.py
((67 lines not shown))
+ P = ceil((M-radius*2)/step)
+ Q = ceil((N-radius*2)/step)
+ R = (rings*histograms + 1)*orientations
+
+ References
+ ----------
+ [1]: Tola et al. "Daisy: An efficient dense descriptor applied to
+ wide-baseline stereo." Pattern Analysis and Machine Intelligence,
+ IEEE Transactions on 32.5 (2010): 815-830.
+ [2]: http://cvlab.epfl.ch/alumni/tola/daisy.html
+ '''
+
+ # Validate image format.
+ if img.ndim > 2:
+ raise ValueError('Only grey-level images are supported.')
+ if img.dtype.kind == 'u':
@stefanv Owner
stefanv added a note

skimage.img_as_float

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stefanv
Owner

Hi Anders, thanks for your contribution! This is exciting--our first real feature detector PR. Would you please add a demo script (such as those in the gallery), so that we could visualize the DAISY descriptors? If they can easily be used for matching, the standard "lines between matching features" demo would be great. Thanks again!

skimage/feature/_daisy.py
@@ -0,0 +1,165 @@
+import numpy as np
+from scipy import sqrt, pi, arctan2, cos, sin, exp
+from scipy.ndimage import gaussian_filter
+from scipy.special import iv
+
+
+def daisy(img, step=4, radius=15, rings=3, histograms=8, orientations=8,
+ normalization='l1', sigmas=None, ring_radii=None):
+ '''Extract DAISY feature descriptors densely for the given image.
+
+ DAISY is a feature descriptor similar to SIFT formulated in a way that
+ allows for fast dense extraction. Typically, this is practical for
+ bag-of-features image representations.
+
+ The implementation follows Tola et al. [1] but deviate on the following
@stefanv Owner
stefanv added a note

[1]_

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
skimage/feature/_daisy.py
((5 lines not shown))
+
+
+def daisy(img, step=4, radius=15, rings=3, histograms=8, orientations=8,
+ normalization='l1', sigmas=None, ring_radii=None):
+ '''Extract DAISY feature descriptors densely for the given image.
+
+ DAISY is a feature descriptor similar to SIFT formulated in a way that
+ allows for fast dense extraction. Typically, this is practical for
+ bag-of-features image representations.
+
+ The implementation follows Tola et al. [1] but deviate on the following
+ points:
+ * Histogram bin contribution are smoothed with a circular Gaussian
+ window over the tonal range (the angular range).
+ * The sigma values of the spatial Gaussian smoothing in this code do
+ not match the sigma values in the original code by Tola et al. [2].
@stefanv Owner
stefanv added a note

[2]_

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
skimage/feature/_daisy.py
((8 lines not shown))
+ normalization='l1', sigmas=None, ring_radii=None):
+ '''Extract DAISY feature descriptors densely for the given image.
+
+ DAISY is a feature descriptor similar to SIFT formulated in a way that
+ allows for fast dense extraction. Typically, this is practical for
+ bag-of-features image representations.
+
+ The implementation follows Tola et al. [1] but deviate on the following
+ points:
+ * Histogram bin contribution are smoothed with a circular Gaussian
+ window over the tonal range (the angular range).
+ * The sigma values of the spatial Gaussian smoothing in this code do
+ not match the sigma values in the original code by Tola et al. [2].
+ In their code, spatial smoothing is applied to both the input image
+ and the center histogram. However, this smoothing is not documented
+ in [1] and, therefore, it is omitted.
@stefanv Owner
stefanv added a note

[1]_

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
skimage/feature/_daisy.py
((37 lines not shown))
+ orientations : int, optional
+ Number of orientations (bins) per histogram.
+ normalization : {'l1', 'l2', 'daisy', 'off'}, optional
+ How to normalize the descriptors:
+ * 'l1': L1-normalization of each descriptor.
+ * 'l2': L2-normalization of each descriptor.
+ * 'daisy': L2-normalization of individual histograms.
+ * 'off': Disable normalization.
+ sigmas : 1D array of float, optional
+ Standard deviation of spatial Gaussian smoothing for the center
+ histogram and for each ring of histograms. The array of sigmas should
+ be sorted from the center and out. I.e. the first sigma value defines
+ the spatial smoothing of the center histogram and the last sigma value
+ defines the spatial smoothing of the outermost ring. Specifying sigmas
+ overrides the following parameter:
+ rings = len(sigmas)-1
@stefanv Owner
stefanv added a note

Place code snippet in double quotes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ahojnnes
Owner

Great contribution, thank you! Would you add an usage example script to doc/examples?

Anders Boesen Lindbo Larsen Better Sphinx documentation. e5fb8a2
@andersbll

Stefan: Alas, my code only performs dense feature extraction - not local feature detection and extraction. Dense feature extraction is useful for e.g. visual recognition based on bag-of-features image representations.

Johannes: I assume your example request is about feature detection/extraction/matching which indeed is very suitable for an example. Dense feature extraction, however, is not that easy to demonstrate. I can try to make a simple visual recognition example, but it will require lots of image data. Would that be ok?

@amueller
Owner

@andersbll This is great :) Do you know of a comparison of Daisy and SIFT for recognition?
Maybe a good example would be one that is sort of similar to the hog one.
You could visualize some of the feature channels for example. Do you think that is useful?

A full-blown recognition example is maybe a bit over-the-top for an example as you'd also need to have a classifier.

@andersbll

@amueller: DAISY and SIFT are very similar by construction and have the same performance (according to the papers below). I prefer DAISY descriptors as they are more flexible in terms of configuration and because the formulation is more elegant than SIFT.
I agree with you that a recognition example is going to be too extensive. On the other hand, I do not think that a HOG image is useful to illustrate DAISY since the spatial layout of the dense features is typically disregarded in a bag-of-features application.
Maybe I can make an illustration similar to: http://www.vlfeat.org/demo/sift_basic_4.jpg ?

DAISY vs. SIFT:

  • Winder, S., Hua, G., & Brown, M. (2009, June). Picking the best daisy. In Computer Vision and Pattern Recognition, 2009. CVPR 2009. IEEE Conference on (pp. 178-185). IEEE.
  • Dahl, A. L., Aanæs, H., & Pedersen, K. S. (2011, May). Finding the Best Feature Detector-Descriptor Combination. In 3D Imaging, Modeling, Processing, Visualization and Transmission (3DIMPVT), 2011 International Conference on (pp. 318-325). IEEE.
  • Kaneva, B., Torralba, A., & Freeman, W. T. (2011, November). Evaluation of image features using a photorealistic virtual world. In Computer Vision (ICCV), 2011 IEEE International Conference on (pp. 2282-2289). IEEE.
@amueller
Owner

Thanks for the references. I didn't know the last one.
I agree that daisy will probably work good, it is just that I don't know of any literature using it for recognition.
As far as I can tell, the references you gave all do keypoint matching, not bag of words.

@andersbll

Sorry, I didn't catch from your first post that you asked for descritor evaluations in recognition applications. Unfortunately, I haven't seen any such evaluations!

@amueller
Owner

Np. It would be great if there was something like that.

I actually have a student on the task of evaluating BRIEF, LBP, DAISY and SIFT for superpixel-bases segmentation. But he is kind of taking his time ;)

Having Daisy in here would definitely make the comparison easier.

Anders Boese... added some commits
Anders Boesen Lindbo Larsen Add circle perimeter to draw module. 2849cbf
Anders Boesen Lindbo Larsen Add DAISY visualization + example plot. 6c19cf0
@andersbll

I have added visualization of the descriptors and an example script to doc/examples:
daisy

I had to implement a circle drawing function since the draw module only supported filled circles.

Anders Boesen Lindbo Larsen Handle all non-float images. 7aa9435
@amueller
Owner

that is pretty cool :)
To bad they don't sit on keypoints ;) But that looks really great!

@andersbll

Yep, interest point description would be useful!
I actually plan to work on interest points in the near future. I will consider doing a Python version if the Matlab code I'm considering goes bad. :)

skimage/feature/_daisy.py
((164 lines not shown))
+ # Normalize descriptors.
+ if normalization != 'off':
+ descs += 1e-10
+ if normalization == 'l1':
+ descs /= np.sum(descs, axis=2)[:, :, np.newaxis]
+ elif normalization == 'l2':
+ descs /= sqrt(np.sum(descs ** 2, axis=2))[:, :, np.newaxis]
+ elif normalization == 'daisy':
+ for i in range(0, desc_dims, orientations):
+ norms = sqrt(np.sum(descs[:, :, i:i + orientations] ** 2,
+ axis=2))
+ descs[:, :, i:i + orientations] /= norms[:, :, np.newaxis]
+
+ if visualize:
+ from skimage import draw
+ from skimage import color
@ahojnnes Owner

Is there a specific reason to have the imports here? In general I would move them to the top.

I copied that code practice from the HOG-implementation. I also prefer imports at the top.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
skimage/feature/_daisy.py
((209 lines not shown))
+ # Draw histogram bins
+ bin_size = descs[i, j, orientations + r_num *
+ histograms * orientations +
+ t_num * orientations + o_num]
+ bin_size /= max_bin
+ dy = sigmas[r_num + 1] * bin_size * sin(o)
+ dx = sigmas[r_num + 1] * bin_size * cos(o)
+ coords = draw.line(hist_y, hist_x, hist_y + dy,
+ hist_x + dx)
+ set_color(descs_img, coords, color)
+ return descs, descs_img
+ else:
+ return descs
+
+
+def set_color(img, coords, color):
@ahojnnes Owner

You should be able to do:

rr, cc = coords
rr_inside = np.logical_and(rr >= 0, rr < img.shape[0])
cc_inside = np.logical_and(cc >= 0, cc < img.shape[1])
inside = np.logical_and(rr_inside, cc_inside)
img[rr[inside], cc[inside]] = color

And I think it might be a good idea to move this function as a public helper function in the draw module. This might be useful for other people as well.

I agree, will do!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ahojnnes
Owner

Very nice contribution!

@andersbll if you are interested in interest point detection, I have an open PR with some of the basic interest point detectors (see #321).

@andersbll

@ahojnnes: Thanks for reference!

Anders Boese... added some commits
Anders Boesen Lindbo Larsen Move set_color() to draw module. 097f25d
Anders Boesen Lindbo Larsen No need to normalize angular smoothing. 3b43cf0
@ahojnnes
Owner

From what I can tell, this PR seems to be ready to merge. Although you still want to make some changes @andersbll?

@andersbll

I believe it's pretty solid by now. Thank you for the comments and corrections.

Anders Boesen Lindbo Larsen Fixed submodule import. c276ec0
@andersbll

Ok, this time i mean it! :)

@stefanv
Owner

I have some code for extracting FAST features. Could DAISY and FAST be combined to do keypoints?

@ahojnnes
Owner

I guess we are done here - thank you very much.

@andersbll If you like, you can add yourself to the list of contributors in CONTRIBUTORS.txt berfore I finally merge this PR?

Anders Boesen Lindbo Larsen Add myself as contributor. 8b6b51e
@andersbll

@stefanv: ahojnnes has already pointed me to some interest point detectors. Unfortunately, it's not that simple to convert dense feature description to interest points as individual interest point description is more complex. Maybe another day/PR! :)

@ahojnnes: I have added myself, thanks.

@ahojnnes
Owner

@andersbll Sorry, but it seems we need another rebase on the current master. Could you do that?

Anders Boese... added some commits
Anders Boesen Lindbo Larsen Add dense DAISY feature description. 4173d16
Anders Boesen Lindbo Larsen PEP8 style corrections. 24d3921
Anders Boesen Lindbo Larsen Better Sphinx documentation. 633443e
Anders Boesen Lindbo Larsen Add circle perimeter to draw module. 2313361
Anders Boesen Lindbo Larsen Add DAISY visualization + example plot. 6770161
Anders Boesen Lindbo Larsen Handle all non-float images. 74e6c38
Anders Boesen Lindbo Larsen Move set_color() to draw module. b770578
Anders Boesen Lindbo Larsen No need to normalize angular smoothing. 8d8b8ef
Anders Boesen Lindbo Larsen Fixed submodule import. 4206804
Anders Boesen Lindbo Larsen Add myself as contributor. 101bcc6
Anders Boesen Lindbo Larsen Merge branch 'master' of github.com:andersbll/scikit-image
Conflicts:
	CONTRIBUTORS.txt
1c674f6
@andersbll

@ahojnnes: I hope this resolves the conflicts you encountered.

@ahojnnes ahojnnes merged commit 7d328ae into scikit-image:master
@ahojnnes
Owner

Merged #384.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 8, 2012
  1. Add dense DAISY feature description.

    Anders Boesen Lindbo Larsen authored
  2. PEP8 style corrections.

    Anders Boesen Lindbo Larsen authored
Commits on Dec 9, 2012
  1. Better Sphinx documentation.

    Anders Boesen Lindbo Larsen authored
Commits on Dec 15, 2012
  1. Add circle perimeter to draw module.

    Anders Boesen Lindbo Larsen authored
  2. Add DAISY visualization + example plot.

    Anders Boesen Lindbo Larsen authored
Commits on Dec 16, 2012
  1. Handle all non-float images.

    Anders Boesen Lindbo Larsen authored
  2. Move set_color() to draw module.

    Anders Boesen Lindbo Larsen authored
  3. No need to normalize angular smoothing.

    Anders Boesen Lindbo Larsen authored
Commits on Dec 17, 2012
  1. Fixed submodule import.

    Anders Boesen Lindbo Larsen authored
Commits on Dec 27, 2012
  1. Add myself as contributor.

    Anders Boesen Lindbo Larsen authored
  2. Add dense DAISY feature description.

    Anders Boesen Lindbo Larsen authored
  3. PEP8 style corrections.

    Anders Boesen Lindbo Larsen authored
  4. Better Sphinx documentation.

    Anders Boesen Lindbo Larsen authored
  5. Add circle perimeter to draw module.

    Anders Boesen Lindbo Larsen authored
  6. Add DAISY visualization + example plot.

    Anders Boesen Lindbo Larsen authored
  7. Handle all non-float images.

    Anders Boesen Lindbo Larsen authored
  8. Move set_color() to draw module.

    Anders Boesen Lindbo Larsen authored
  9. No need to normalize angular smoothing.

    Anders Boesen Lindbo Larsen authored
  10. Fixed submodule import.

    Anders Boesen Lindbo Larsen authored
  11. Add myself as contributor.

    Anders Boesen Lindbo Larsen authored
  12. Merge branch 'master' of github.com:andersbll/scikit-image

    Anders Boesen Lindbo Larsen authored
    Conflicts:
    	CONTRIBUTORS.txt
This page is out of date. Refresh to see the latest.
View
3  CONTRIBUTORS.txt
@@ -126,3 +126,6 @@
- Steven Silvester, Karel Zuiderveld
Adaptive Histogram Equalization
+
+- Anders Boesen Lindbo Larsen
+ Dense DAISY feature description, circle perimeter drawing.
View
28 doc/examples/plot_daisy.py
@@ -0,0 +1,28 @@
+"""
+===============================
+Dense DAISY feature description
+===============================
+
+The DAISY local image descriptor is based on gradient orientation histograms
+similar to the SIFT descriptor. It is formulated in a way that allows for fast
+dense extraction which is useful for e.g. bag-of-features image
+representations.
+
+In this example a limited number of DAISY descriptors are extracted at a large
+scale for illustrative purposes.
+"""
+
+from skimage.feature import daisy
+from skimage import data
+import matplotlib.pyplot as plt
+
+
+img = data.camera()
+descs, descs_img = daisy(img, step=180, radius=58, rings=2, histograms=6,
+ orientations=8, visualize=True)
+
+plt.axis('off')
+plt.imshow(descs_img)
+descs_num = descs.shape[0] * descs.shape[1]
+plt.title('%i DAISY descriptors extracted:' % descs_num)
+plt.show()
View
8 doc/examples/plot_shapes.py
@@ -13,7 +13,7 @@
import matplotlib.pyplot as plt
-from skimage.draw import line, polygon, circle, ellipse
+from skimage.draw import line, polygon, circle, circle_perimeter, ellipse
import numpy as np
@@ -42,5 +42,9 @@
rr, cc = ellipse(300, 300, 100, 200, img.shape)
img[rr,cc,2] = 255
+# circle
+rr, cc = circle_perimeter(120, 400, 50)
+img[rr, cc, :] = (255, 0, 255)
+
plt.imshow(img)
-plt.show()
+plt.show()
View
2  skimage/draw/__init__.py
@@ -1,2 +1,2 @@
-from ._draw import line, polygon, ellipse, circle
+from ._draw import line, polygon, ellipse, circle, circle_perimeter, set_color
bresenham = line
View
66 skimage/draw/_draw.pyx
@@ -187,3 +187,69 @@ def circle(double cy, double cx, double radius, shape=None):
``img[rr, cc] = 1``.
"""
return ellipse(cy, cx, radius, radius, shape)
+
+
+def circle_perimeter(int cy, int cx, int radius):
+ """Generate circle perimeter coordinates.
+
+ Parameters
+ ----------
+ cy, cx : int
+ Centre coordinate of circle.
+ radius: int
+ Radius of circle.
+
+ Returns
+ -------
+ rr, cc : (N,) ndarray of int
+ Indices of pixels that belong to the circle perimeter.
+ May be used to directly index into an array, e.g.
+ ``img[rr, cc] = 1``.
+
+ """
+
+ cdef list rr = list()
+ cdef list cc = list()
+
+ cdef int x = 0
+ cdef int y = radius
+ cdef int d = 3 - 2 * radius
+
+ while y >= x:
+ rr.extend([y, -y, y, -y, x, -x, x, -x])
+ cc.extend([x, x, -x, -x, y, y, -y, -y])
+ if d < 0:
+ d += 4 * x + 6
+ else:
+ d += 4 * (x - y) + 10
+ y -= 1
+ x += 1
+
+ return np.array(rr) + cy, np.array(cc) + cx
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def set_color(img, coords, color):
+ """Set pixel color in the image at the given coordiantes. Coordinates that
+ exceeed the shape of the image will be ignored.
+
+ Parameters
+ ----------
+ img : (M, N, D) ndarray
+ Image
+ coords : ((P,) ndarray, (P,) ndarray)
+ Coordinates of pixels to be colored.
+ color : (D,) ndarray
+ Color to be assigned to coordinates in the image.
+
+ Returns
+ -------
+ img : (M, N, D) ndarray
+ The updated image.
+ """
+ rr, cc = coords
+ rr_inside = np.logical_and(rr >= 0, rr < img.shape[0])
+ cc_inside = np.logical_and(cc >= 0, cc < img.shape[1])
+ inside = np.logical_and(rr_inside, cc_inside)
+ img[rr[inside], cc[inside]] = color
View
33 skimage/draw/tests/test_draw.py
@@ -1,7 +1,7 @@
from numpy.testing import assert_array_equal
import numpy as np
-from skimage.draw import line, polygon, circle, ellipse
+from skimage.draw import line, polygon, circle, circle_perimeter, ellipse
def test_line_horizontal():
@@ -150,6 +150,37 @@ def test_circle():
assert_array_equal(img, img_)
+def test_circle_perimeter():
+ img = np.zeros((15, 15), 'uint8')
+ rr, cc = circle_perimeter(7, 7, 0)
+ img[rr, cc] = 1
+ assert(np.sum(img) == 1)
+
+ img = np.zeros((17, 15), 'uint8')
+ rr, cc = circle_perimeter(7, 7, 7)
+ img[rr, cc] = 1
+ img_ = np.array(
+ [[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
+ [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
+ [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
+ [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
+ [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
+ [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
+ [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
+ )
+ assert_array_equal(img, img_)
+
+
def test_ellipse():
img = np.zeros((15, 15), 'uint8')
View
1  skimage/feature/__init__.py
@@ -1,3 +1,4 @@
+from ._daisy import daisy
from ._hog import hog
from .texture import greycomatrix, greycoprops, local_binary_pattern
from .peak import peak_local_max
View
217 skimage/feature/_daisy.py
@@ -0,0 +1,217 @@
+import numpy as np
+from scipy import sqrt, pi, arctan2, cos, sin, exp
+from scipy.ndimage import gaussian_filter
+import skimage.color
+from skimage import img_as_float, draw
+
+
+def daisy(img, step=4, radius=15, rings=3, histograms=8, orientations=8,
+ normalization='l1', sigmas=None, ring_radii=None, visualize=False):
+ '''Extract DAISY feature descriptors densely for the given image.
+
+ DAISY is a feature descriptor similar to SIFT formulated in a way that
+ allows for fast dense extraction. Typically, this is practical for
+ bag-of-features image representations.
+
+ The implementation follows Tola et al. [1]_ but deviate on the following
+ points:
+
+ * Histogram bin contribution are smoothed with a circular Gaussian
+ window over the tonal range (the angular range).
+ * The sigma values of the spatial Gaussian smoothing in this code do not
+ match the sigma values in the original code by Tola et al. [2]_. In
+ their code, spatial smoothing is applied to both the input image and
+ the center histogram. However, this smoothing is not documented in [1]_
+ and, therefore, it is omitted.
+
+ Parameters
+ ----------
+ img : (M, N) array
+ Input image (greyscale).
+ step : int, optional
+ Distance between descriptor sampling points.
+ radius : int, optional
+ Radius (in pixels) of the outermost ring.
+ rings : int, optional
+ Number of rings.
+ histograms : int, optional
+ Number of histograms sampled per ring.
+ orientations : int, optional
+ Number of orientations (bins) per histogram.
+ normalization : [ 'l1' | 'l2' | 'daisy' | 'off' ], optional
+ How to normalize the descriptors
+
+ * 'l1': L1-normalization of each descriptor.
+ * 'l2': L2-normalization of each descriptor.
+ * 'daisy': L2-normalization of individual histograms.
+ * 'off': Disable normalization.
+
+ sigmas : 1D array of float, optional
+ Standard deviation of spatial Gaussian smoothing for the center
+ histogram and for each ring of histograms. The array of sigmas should
+ be sorted from the center and out. I.e. the first sigma value defines
+ the spatial smoothing of the center histogram and the last sigma value
+ defines the spatial smoothing of the outermost ring. Specifying sigmas
+ overrides the following parameter.
+ ``rings = len(sigmas)-1``
+
+ ring_radii : 1D array of int, optional
+ Radius (in pixels) for each ring. Specifying ring_radii overrides the
+ following two parameters.
+ | ``rings = len(ring_radii)``
+ | ``radius = ring_radii[-1]``
+
+ If both sigmas and ring_radii are given, they must satisfy the
+ following predicate since no radius is needed for the center
+ histogram.
+ ``len(ring_radii) == len(sigmas)+1``
+ visualize : bool, optional
+ Generate a visualization of the DAISY descriptors
+
+
+ Returns
+ -------
+ descs : array
+ Grid of DAISY descriptors for the given image as an array
+ dimensionality (P, Q, R) where
+ | ``P = ceil((M-radius*2)/step)``
+ | ``Q = ceil((N-radius*2)/step)``
+ | ``R = (rings*histograms + 1)*orientations``
+ descs_img : (M, N, 3) array (only if visualize==True)
+ Visualization of the DAISY descriptors.
+
+ References
+ ----------
+ .. [1] Tola et al. "Daisy: An efficient dense descriptor applied to wide-
+ baseline stereo." Pattern Analysis and Machine Intelligence, IEEE
+ Transactions on 32.5 (2010): 815-830.
+ .. [2] http://cvlab.epfl.ch/alumni/tola/daisy.html
+ '''
+
+ # Validate image format.
+ if img.ndim > 2:
+ raise ValueError('Only grey-level images are supported.')
+ if img.dtype.kind != 'f':
+ img = img_as_float(img)
+
+ # Validate parameters.
+ if sigmas is not None and ring_radii is not None \
+ and len(sigmas) - 1 != len(ring_radii):
+ raise ValueError('len(sigmas)-1 != len(ring_radii)')
+ if ring_radii is not None:
+ rings = len(ring_radii)
+ radius = ring_radii[-1]
+ if sigmas is not None:
+ rings = len(sigmas) - 1
+ if sigmas is None:
+ sigmas = [radius * (i + 1) / float(2 * rings) for i in range(rings)]
+ if ring_radii is None:
+ ring_radii = [radius * (i + 1) / float(rings) for i in range(rings)]
+ if normalization not in ['l1', 'l2', 'daisy', 'off']:
+ raise ValueError('Invalid normalization method.')
+
+ # Compute image derivatives.
+ dx = np.zeros(img.shape)
+ dy = np.zeros(img.shape)
+ dx[:, :-1] = np.diff(img, n=1, axis=1)
+ dy[:-1, :] = np.diff(img, n=1, axis=0)
+
+ # Compute gradient orientation and magnitude and their contribution
+ # to the histograms.
+ grad_mag = sqrt(dx ** 2 + dy ** 2)
+ grad_ori = arctan2(dy, dx)
+ orientation_kappa = orientations / pi
+ orientation_angles = [2 * o * pi / orientations - pi
+ for o in range(orientations)]
+ hist = np.empty((orientations,) + img.shape, dtype=float)
+ for i, o in enumerate(orientation_angles):
+ # Weigh bin contribution by the circular normal distribution
+ hist[i, :, :] = exp(orientation_kappa * cos(grad_ori - o))
+ # Weigh bin contribution by the gradient magnitude
+ hist[i, :, :] = np.multiply(hist[i, :, :], grad_mag)
+
+ # Smooth orientation histograms for the center and all rings.
+ sigmas = [sigmas[0]] + sigmas
+ hist_smooth = np.empty((rings + 1,) + hist.shape, dtype=float)
+ for i in range(rings + 1):
+ for j in range(orientations):
+ hist_smooth[i, j, :, :] = gaussian_filter(hist[j, :, :],
+ sigma=sigmas[i])
+
+ # Assemble descriptor grid.
+ theta = [2 * pi * j / histograms for j in range(histograms)]
+ desc_dims = (rings * histograms + 1) * orientations
+ descs = np.empty((desc_dims, img.shape[0] - 2 * radius,
+ img.shape[1] - 2 * radius))
+ descs[:orientations, :, :] = hist_smooth[0, :, radius:-radius,
+ radius:-radius]
+ idx = orientations
+ for i in range(rings):
+ for j in range(histograms):
+ y_min = radius + int(round(ring_radii[i] * sin(theta[j])))
+ y_max = descs.shape[1] + y_min
+ x_min = radius + int(round(ring_radii[i] * cos(theta[j])))
+ x_max = descs.shape[2] + x_min
+ descs[idx:idx + orientations, :, :] = hist_smooth[i + 1, :,
+ y_min:y_max,
+ x_min:x_max]
+ idx += orientations
+ descs = descs[:, ::step, ::step]
+ descs = descs.swapaxes(0, 1).swapaxes(1, 2)
+
+ # Normalize descriptors.
+ if normalization != 'off':
+ descs += 1e-10
+ if normalization == 'l1':
+ descs /= np.sum(descs, axis=2)[:, :, np.newaxis]
+ elif normalization == 'l2':
+ descs /= sqrt(np.sum(descs ** 2, axis=2))[:, :, np.newaxis]
+ elif normalization == 'daisy':
+ for i in range(0, desc_dims, orientations):
+ norms = sqrt(np.sum(descs[:, :, i:i + orientations] ** 2,
+ axis=2))
+ descs[:, :, i:i + orientations] /= norms[:, :, np.newaxis]
+
+ if visualize:
+ descs_img = skimage.color.gray2rgb(img)
+ for i in range(descs.shape[0]):
+ for j in range(descs.shape[1]):
+ # Draw center histogram sigma
+ color = (1, 0, 0)
+ desc_y = i * step + radius
+ desc_x = j * step + radius
+ coords = draw.circle_perimeter(desc_y, desc_x, sigmas[0])
+ draw.set_color(descs_img, coords, color)
+ max_bin = np.max(descs[i, j, :])
+ for o_num, o in enumerate(orientation_angles):
+ # Draw center histogram bins
+ bin_size = descs[i, j, o_num] / max_bin
+ dy = sigmas[0] * bin_size * sin(o)
+ dx = sigmas[0] * bin_size * cos(o)
+ coords = draw.line(desc_y, desc_x, desc_y + dy,
+ desc_x + dx)
+ draw.set_color(descs_img, coords, color)
+ for r_num, r in enumerate(ring_radii):
+ color_offset = float(1 + r_num) / rings
+ color = (1 - color_offset, 1, color_offset)
+ for t_num, t in enumerate(theta):
+ # Draw ring histogram sigmas
+ hist_y = desc_y + int(round(r * sin(t)))
+ hist_x = desc_x + int(round(r * cos(t)))
+ coords = draw.circle_perimeter(hist_y, hist_x,
+ sigmas[r_num + 1])
+ draw.set_color(descs_img, coords, color)
+ for o_num, o in enumerate(orientation_angles):
+ # Draw histogram bins
+ bin_size = descs[i, j, orientations + r_num *
+ histograms * orientations +
+ t_num * orientations + o_num]
+ bin_size /= max_bin
+ dy = sigmas[r_num + 1] * bin_size * sin(o)
+ dx = sigmas[r_num + 1] * bin_size * cos(o)
+ coords = draw.line(hist_y, hist_x, hist_y + dy,
+ hist_x + dx)
+ draw.set_color(descs_img, coords, color)
+ return descs, descs_img
+ else:
+ return descs
View
95 skimage/feature/tests/test_daisy.py
@@ -0,0 +1,95 @@
+import numpy as np
+from numpy.testing import assert_raises, assert_almost_equal
+from numpy import sqrt, ceil
+
+from skimage import data
+from skimage import img_as_float
+from skimage.feature import daisy
+
+
+def test_daisy_color_image_unsupported_error():
+ img = np.zeros((20, 20, 3))
+ assert_raises(ValueError, daisy, img)
+
+
+def test_daisy_desc_dims():
+ img = img_as_float(data.lena()[:128, :128].mean(axis=2))
+ rings = 2
+ histograms = 4
+ orientations = 3
+ descs = daisy(img, rings=rings, histograms=histograms,
+ orientations=orientations)
+ assert(descs.shape[2] == (rings * histograms + 1) * orientations)
+
+ rings = 4
+ histograms = 5
+ orientations = 13
+ descs = daisy(img, rings=rings, histograms=histograms,
+ orientations=orientations)
+ assert(descs.shape[2] == (rings * histograms + 1) * orientations)
+
+
+def test_descs_shape():
+ img = img_as_float(data.lena()[:256, :256].mean(axis=2))
+ radius = 20
+ step = 8
+ descs = daisy(img, radius=radius, step=step)
+ assert(descs.shape[0] == ceil((img.shape[0] - radius * 2) / float(step)))
+ assert(descs.shape[1] == ceil((img.shape[1] - radius * 2) / float(step)))
+
+ img = img[:-1, :-2]
+ radius = 5
+ step = 3
+ descs = daisy(img, radius=radius, step=step)
+ assert(descs.shape[0] == ceil((img.shape[0] - radius * 2) / float(step)))
+ assert(descs.shape[1] == ceil((img.shape[1] - radius * 2) / float(step)))
+
+
+def test_daisy_incompatible_sigmas_and_radii():
+ img = img_as_float(data.lena()[:128, :128].mean(axis=2))
+ sigmas = [1, 2]
+ radii = [1, 2]
+ assert_raises(ValueError, daisy, img, sigmas=sigmas, ring_radii=radii)
+
+
+def test_daisy_normalization():
+ img = img_as_float(data.lena()[:64, :64].mean(axis=2))
+
+ descs = daisy(img, normalization='l1')
+ for i in range(descs.shape[0]):
+ for j in range(descs.shape[1]):
+ assert_almost_equal(np.sum(descs[i, j, :]), 1)
+ descs_ = daisy(img)
+ assert_almost_equal(descs, descs_)
+
+ descs = daisy(img, normalization='l2')
+ for i in range(descs.shape[0]):
+ for j in range(descs.shape[1]):
+ assert_almost_equal(sqrt(np.sum(descs[i, j, :] ** 2)), 1)
+
+ orientations = 8
+ descs = daisy(img, orientations=orientations, normalization='daisy')
+ desc_dims = descs.shape[2]
+ for i in range(descs.shape[0]):
+ for j in range(descs.shape[1]):
+ for k in range(0, desc_dims, orientations):
+ assert_almost_equal(sqrt(np.sum(
+ descs[i, j, k:k + orientations] ** 2)), 1)
+
+ img = np.zeros((50, 50))
+ descs = daisy(img, normalization='off')
+ for i in range(descs.shape[0]):
+ for j in range(descs.shape[1]):
+ assert_almost_equal(np.sum(descs[i, j, :]), 0)
+
+ assert_raises(ValueError, daisy, img, normalization='does_not_exist')
+
+
+def test_daisy_visualization():
+ img = img_as_float(data.lena()[:128, :128].mean(axis=2))
+ descs, descs_img = daisy(img, visualize=True)
+ assert(descs_img.shape == (128, 128, 3))
+
+if __name__ == '__main__':
+ from numpy import testing
+ testing.run_module_suite()
Something went wrong with that request. Please try again.