This file is indexed.

/usr/lib/python3/dist-packages/dtcwt/keypoint.py is in python3-dtcwt 0.11.0-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
from __future__ import absolute_import

import numpy as np

from dtcwt.sampling import upsample_highpass, upsample

__all__ = [ 'find_keypoints' ]

def find_keypoints(highpass_highpasses, method=None,
        alpha=1.0, beta=0.4, kappa=1.0/6.0,
        threshold=None, max_points=None,
        upsample_keypoint_energy=None, upsample_highpasses=None,
        refine_positions=True, skip_levels=1):
    """
    :param highpass_highpasses: (NxMx6) matrix of highpass subband images
    :param method: *(optional)* string specifying which keypoint energy method to use
    :param alpha: *(optional)* scale parameter for ``'fauqueur'`` method
    :param beta: *(optional)* shape parameter for ``'fauqueur'`` method
    :param kappa: *(optiona)* suppression parameter for ``'kingsbury'`` method
    :param threshold: *(optional)* minimum keypoint energy of returned keypoints
    :param max_points: *(optional)* maximum number of keypoints to return
    :param upsample_keypoint_energy: is non-None, a string specifying a method used to upscale the keypoint energy map before finding keypoints
    :param upsample_subands: is non-None, a string specifying a method used to upscale the subband image before finding keypoints
    :param refine_positions: *(optional)* should the keypoint positions be refined to sub-pixel accuracy
    :param skip_levels: *(optional)* number of levels of the transform to ignore before looking for keypoints

    :returns: (Px4) array of P keypoints in image co-ordinates

    .. warning::

        The interface and behaviour of this function is the subject of an open
        research project. It is provided in this release as a preview of
        forthcoming functionality but it is subject to change between releases.

    The rows of the returned keypoint array give the x co-ordinate, y
    co-ordinate, scale and keypoint energy. The rows are sorted in order of
    decreasing keypoint energy.

    If *refine_positions* is ``True`` then the positions (and energy) of the
    keypoints will be refined to sub-pixel accuracy by fitting a quadratic
    patch. If *refine_positions* is ``False`` then the keypoint locations will
    be those corresponding directly to pixel-wise maxima of the subband images.

    The *max_points* and *threshold* parameters are cumulative: if both are
    specified then the *max_points* greatest energy keypoints with energy
    greater than *threshold* will be returned.

    Usually the keypoint energies returned from the finest scale level are
    dominated by noise and so one usually wants to specify *skip_levels* to be
    1 or 2. If *skip_levels* is 0 then all levels will be used to compute
    keypoint energy.

    The *upsample_highpasses* and *upsample_keypoint_energy* parameters are used
    to control whether the individual subband coefficients and/org the keypoint
    energy map are upscaled by 2 before finding keypoints. If these parameters
    are None then no corresponding upscaling is performed. If non-None they
    specify the upscale method as outlined in
    :py:func:`dtcwt.sampling.upsample`.

    If *method* is ``None``, the default ``'fauqueur'`` method is used.

    =========== ======================================= ======================
    Name        Description                             Parameters used
    =========== ======================================= ======================
    fauqueur    Geometric mean of absolute values[1]    *alpha*, *beta*
    bendale     Minimum absolute value[2]               none
    kingsbury   Cross-product of orthogonal highpasses    *kappa*
    =========== ======================================= ======================

    [1] Julien Fauqueur, Nick Kingsbury, and Ryan Anderson. *Multiscale
    Keypoint Detection using the Dual-Tree Complex Wavelet Transform*. 2006
    International Conference on Image Processing, pages 1625-1628, October
    2006. ISSN 1522-4880. doi: 10.1109/ICIP.2006.312656.
    http://ieeexplore.ieee.org/lpdocs/epic03/wrapper.htm?arnumber=4106857.

    [2] Pashmina Bendale, Bill Triggs, and Nick Kingsbury. *Multiscale Keypoint
    Analysis based on Complex Wavelets*. In British Machine Vision Con-ference
    (BMVC), 2010.
    http://www-sigproc.eng.cam.ac.uk/~pb397/publications/BTK_BMVC_2010_abstract.pdf.
    """
    # Set default method
    if method is None:
        method = 'fauqueur'

    # Skip levels
    highpass_highpasses = highpass_highpasses[skip_levels:]

    # Compute contribution to scale from upsampling
    upsample_scale = 1
    if upsample_highpasses is not None:
        upsample_scale <<= 1
    if upsample_keypoint_energy is not None:
        upsample_scale <<= 1

    # Find keypoint energy map for each level
    kp_energies = []
    for subband in highpass_highpasses:
        if upsample_highpasses is not None:
            subband = upsample_highpass(subband, upsample_highpasses)

        if method == 'fauqueur':
            kp_energies.append(_keypoint_energy_fauqueur(subband, alpha, beta))
        elif method == 'bendale':
            kp_energies.append(_keypoint_energy_bendale(subband))
        elif method == 'kingsbury':
            kp_energies.append(_keypoint_energy_kingsbury(subband, kappa))
        elif method == 'gale':
            kp_energies.append(_keypoint_energy_gale(subband))
        else:
            raise ValueError('Unknown method: {0}'.format(method))

        if upsample_keypoint_energy is not None:
            kp_energies[-1] = upsample(kp_energies[-1], upsample_keypoint_energy)

    # Find keypoints for each level
    kps = None
    for level_idx, kp_energy in enumerate(kp_energies):
        kp_scale = 2**(level_idx+1+skip_levels) / float(upsample_scale)
        kp_rows, kp_cols, kp_energies = _kp_energy_maxima(kp_energy, threshold=threshold, refine=refine_positions)

        # Scaling is a bit non-trivial. If the subband has pixel coords in range {0, .., M-1} then it has extent
        # (-0.5, M-0.5]. If we need to scale the pixel size by kp_scale then the final image will have extent
        # (-0.5, kp_scale*M-0.5]. So we need a linear function which maps -0.5 -> -0.5 and M-0.5 -> kp_scale*M-0.5
        # such a function is x -> kp_scale * (x+0.5) - 0.5

        level_kps = np.array((
            (kp_cols+0.5)*kp_scale-0.5, (kp_rows+0.5)*kp_scale-0.5,
            kp_scale*np.ones(kp_cols.shape[0]), kp_energies)).T

        if kps is None:
            kps = level_kps
        else:
            kps = np.vstack((kps, level_kps))

    # Sort keypoints
    sorted_indices = np.argsort(kps[:, 3])
    kps = kps[sorted_indices[::-1],:]

    # Truncate if necessary
    if max_points is not None:
        kps = kps[:max_points]

    # Return keypoints
    return kps

def _keypoint_energy_fauqueur(subband, alpha, beta):
    return alpha * np.power(np.maximum(0, np.product(np.abs(subband), axis=2)), beta)

def _keypoint_energy_bendale(subband):
    return np.min(np.abs(subband), axis=2)

def _keypoint_energy_kingsbury(subband, kappa=1.0/6.0, epsilon=1e-8):
    abs_Y = np.abs(subband)
    A = np.sqrt(np.sum(abs_Y*abs_Y, axis=2))
    B = np.sum(abs_Y[:,:,:3] * abs_Y[:,:,3:], axis=2)

    # The max(0, ...) is not part of the original energy calculation but we use
    # it to avoid finding false maxima in no-threshold cases.
    return np.maximum(0, B/np.maximum(epsilon, A) - kappa*A)

def _keypoint_energy_gale(subband):
    raise NotImplementedError('not implemented yet')

def _nullspace(A, atol=1e-13, rtol=0):
    """Compute an approximate basis for the nullspace of A.

    The algorithm used by this function is based on the singular value
    decomposition of `A`.

    Parameters
    ----------
    A : ndarray
        A should be at most 2-D.  A 1-D array with length k will be treated
        as a 2-D with shape (1, k)
    atol : float
        The absolute tolerance for a zero singular value.  Singular values
        smaller than `atol` are considered to be zero.
    rtol : float
        The relative tolerance.  Singular values less than rtol*smax are
        considered to be zero, where smax is the largest singular value.

    If both `atol` and `rtol` are positive, the combined tolerance is the
    maximum of the two; that is::
        tol = max(atol, rtol * smax)
    Singular values smaller than `tol` are considered to be zero.

    Return value
    ------------
    ns : ndarray
        If `A` is an array with shape (m, k), then `ns` will be an array
        with shape (k, n), where n is the estimated dimension of the
        nullspace of `A`.  The columns of `ns` are a basis for the
        nullspace; each element in numpy.dot(A, ns) will be approximately
        zero.
    """

    A = np.atleast_2d(A)
    u, s, vh = np.linalg.svd(A)
    tol = max(atol, rtol * s[0])
    nnz = (s >= tol).sum()
    ns = vh[nnz:].conj().T
    return ns

def _kp_energy_maxima(X, threshold=None, refine=True):
    # If no threshold is provided, choose one which all keypoints will pass
    if threshold is None:
        threshold = X.min() - 1

    # Compute local maximum image
    maxima = np.ones_like(X) * threshold
    for dx in (-1,0,1):
        for dy in (-1,0,1):
            maxima[1:-2,1:-2] = np.maximum(maxima[1:-2,1:-2], X[1+dy:X.shape[0]-2+dy, 1+dx:X.shape[1]-2+dx])

    # This will be used to store the values of local maxima
    lm_values = []

    # This will be used to store the refined positions
    lm_refined_rows, lm_refined_cols = [], []
            
    # Find local maxima
    lm_rows, lm_cols = np.nonzero(maxima == X)
    
    if refine:
        # Taylor series of I(x) around x_0 is I(x) ~= I(x_o) + dI/dx x + dI^2/dx^2 x^2 + ...
        # maximum is at differential is zero or:
        # 0 = dI/dx + 2 dI^2/dx^2 x => x = -dI/dx * (2*dI^2/dx^2)^-1

        # Form the various gradient images for X
        dXdy, dXdx = np.gradient(X)
        dX2dxdy, dX2dx2 = np.gradient(dXdx)
        dX2dy2, dX2dydx = np.gradient(dXdy)
        
        a_im = np.dstack((
           dX2dx2, dX2dy2, dX2dxdy, dXdx, dXdy, X,
        ))
    
    # Calculate a vectors for each neighbourhood
    for r, c in zip(lm_rows, lm_cols):
        if refine:
            a = a_im[r,c,:]
            A = np.array(((2*a[0], a[2], a[3]), (a[2], 2*a[1], a[4])))
            v = _nullspace(A)[:,0]
            v /= v[2]
            
            # Only accept fittings where maximum is within half of a pixel of
            # the maximum pixel's centre.
            if np.any(np.abs(v[:2]) > 0.5):
                continue
        
            x, y = v[:2]
            lm_values.append(a[0]*x*x + a[1]*y*y + a[2]*x*y + a[3]*x + a[4]*y + a[5])
        else:
            x, y = 0, 0
            lm_values.append(X[r,c])

        lm_refined_rows.append(r+y)
        lm_refined_cols.append(c+x)
    
    return np.array(lm_refined_rows), np.array(lm_refined_cols), np.array(lm_values)