3
import numpy.fft as dft
18
def _condition_inputs(data, kernel):
19
data, kernel = num.asarray(data), num.asarray(kernel)
20
if num.rank(data) == 0:
22
if num.rank(kernel) == 0:
24
if num.rank(data) > 1 or num.rank(kernel) > 1:
25
raise ValueError("arrays must be 1D")
26
if len(data) < len(kernel):
27
data, kernel = kernel, data
30
def correlate(data, kernel, mode=FULL):
31
"""correlate(data, kernel, mode=FULL)
33
>>> correlate(num.arange(8), [1, 2], mode=VALID)
34
array([ 2, 5, 8, 11, 14, 17, 20])
35
>>> correlate(num.arange(8), [1, 2], mode=SAME)
36
array([ 0, 2, 5, 8, 11, 14, 17, 20])
37
>>> correlate(num.arange(8), [1, 2], mode=FULL)
38
array([ 0, 2, 5, 8, 11, 14, 17, 20, 7])
39
>>> correlate(num.arange(8), [1, 2, 3], mode=VALID)
40
array([ 8, 14, 20, 26, 32, 38])
41
>>> correlate(num.arange(8), [1, 2, 3], mode=SAME)
42
array([ 3, 8, 14, 20, 26, 32, 38, 20])
43
>>> correlate(num.arange(8), [1, 2, 3], mode=FULL)
44
array([ 0, 3, 8, 14, 20, 26, 32, 38, 20, 7])
45
>>> correlate(num.arange(8), [1, 2, 3, 4, 5, 6], mode=VALID)
47
>>> correlate(num.arange(8), [1, 2, 3, 4, 5, 6], mode=SAME)
48
array([ 17, 32, 50, 70, 91, 112, 85, 60])
49
>>> correlate(num.arange(8), [1, 2, 3, 4, 5, 6], mode=FULL)
50
array([ 0, 6, 17, 32, 50, 70, 91, 112, 85, 60, 38, 20, 7])
51
>>> correlate(num.arange(8), 1+1j)
52
Traceback (most recent call last):
54
TypeError: array cannot be safely cast to required type
57
data, kernel = _condition_inputs(data, kernel)
60
even = (lenk % 2 == 0)
63
if mode in convolution_modes.keys():
64
mode = convolution_modes[ mode ]
66
result_type = max(kernel.dtype.name, data.dtype.name)
69
wdata = num.concatenate((kdata, data, kdata))
70
result = wdata.astype(result_type)
71
_correlate.Correlate1d(kernel, wdata, result)
72
return result[lenk+halfk:-lenk-halfk+even]
74
wdata = num.concatenate((kdata, data, kdata))
75
result = wdata.astype(result_type)
76
_correlate.Correlate1d(kernel, wdata, result)
77
return result[lenk:-lenk]
79
wdata = num.concatenate((kdata, data, kdata))
80
result = wdata.astype(result_type)
81
_correlate.Correlate1d(kernel, wdata, result)
82
return result[halfk+1:-halfk-1+even]
84
result = data.astype(result_type)
85
_correlate.Correlate1d(kernel, data, result)
88
raise ValueError("Invalid convolution mode.")
90
cross_correlate = correlate
99
def convolve(data, kernel, mode=FULL):
100
"""convolve(data, kernel, mode=FULL)
101
Returns the discrete, linear convolution of 1-D
102
sequences a and v; mode can be 0 (VALID), 1 (SAME), or 2 (FULL)
103
to specify size of the resulting sequence.
105
>>> convolve(num.arange(8), [1, 2], mode=VALID)
106
array([ 1, 4, 7, 10, 13, 16, 19])
107
>>> convolve(num.arange(8), [1, 2], mode=SAME)
108
array([ 0, 1, 4, 7, 10, 13, 16, 19])
109
>>> convolve(num.arange(8), [1, 2], mode=FULL)
110
array([ 0, 1, 4, 7, 10, 13, 16, 19, 14])
111
>>> convolve(num.arange(8), [1, 2, 3], mode=VALID)
112
array([ 4, 10, 16, 22, 28, 34])
113
>>> convolve(num.arange(8), [1, 2, 3], mode=SAME)
114
array([ 1, 4, 10, 16, 22, 28, 34, 32])
115
>>> convolve(num.arange(8), [1, 2, 3], mode=FULL)
116
array([ 0, 1, 4, 10, 16, 22, 28, 34, 32, 21])
117
>>> convolve(num.arange(8), [1, 2, 3, 4, 5, 6], mode=VALID)
119
>>> convolve(num.arange(8), [1, 2, 3, 4, 5, 6], mode=SAME)
120
array([ 4, 10, 20, 35, 56, 77, 90, 94])
121
>>> convolve(num.arange(8), [1, 2, 3, 4, 5, 6], mode=FULL)
122
array([ 0, 1, 4, 10, 20, 35, 56, 77, 90, 94, 88, 71, 42])
123
>>> convolve([1.,2.], num.arange(10.))
124
array([ 0., 1., 4., 7., 10., 13., 16., 19., 22., 25., 18.])
126
data, kernel = _condition_inputs(data, kernel)
127
if len(data) >= len(kernel):
128
return correlate(data, kernel[::-1], mode)
130
return correlate(kernel, data[::-1], mode)
133
def _gaussian(sigma, mew, npoints, sigmas):
134
ox = num.arange(mew-sigmas*sigma,
136
2*sigmas*sigma/npoints, type=num.float64)
142
return ox, 1/(sigma * num.sqrt(2*num.pi)) * x
144
def _correlate2d_fft(data0, kernel0, output=None, mode="nearest", cval=0.0):
145
"""_correlate2d_fft does 2d correlation of 'data' with 'kernel', storing
146
the result in 'output' using the FFT to perform the correlation.
148
supported 'mode's include:
149
'nearest' elements beyond boundary come from nearest edge pixel.
150
'wrap' elements beyond boundary come from the opposite array edge.
151
'reflect' elements beyond boundary come from reflection on same array edge.
152
'constant' elements beyond boundary are set to 'cval'
155
kshape = kernel0.shape
156
oversized = (num.array(shape) + num.array(kshape))
161
kernel = num.zeros(oversized, dtype=num.float64)
162
kernel[:kshape[0], :kshape[1]] = kernel0[::-1,::-1] # convolution <-> correlation
163
data = iraf_frame.frame(data0, oversized, mode=mode, cval=cval)
165
complex_result = (isinstance(data, num.complexfloating) or
166
isinstance(kernel, num.complexfloating))
168
Fdata = dft.fft2(data)
171
Fkernel = dft.fft2(kernel)
174
num.multiply(Fdata, Fkernel, Fdata)
178
convolved = dft.irfft2( Fdata, s=oversized)
180
convolved = dft.irfft2( Fdata, s=oversized)
182
result = convolved[ kshape[0]-1:shape[0]+kshape[0]-1, kshape[1]-1:shape[1]+kshape[1]-1 ]
184
if output is not None:
185
output._copyFrom( result )
190
def _correlate2d_naive(data, kernel, output=None, mode="nearest", cval=0.0):
191
return _correlate.Correlate2d(kernel, data, output, pix_modes[mode], cval)
193
def _fix_data_kernel(data, kernel):
194
"""The _correlate.Correlate2d C-code can only handle kernels which
195
fit inside the data array. Since convolution and correlation are
196
commutative, _fix_data_kernel reverses kernel and data if necessary
197
and panics if there's no good order.
199
data, kernel = map(num.asarray, [data, kernel])
200
if num.rank(data) == 0:
202
elif num.rank(data) == 1:
203
data.shape = (1,) + data.shape
204
if num.rank(kernel) == 0:
206
elif num.rank(kernel) == 1:
207
kernel.shape = (1,) + kernel.shape
208
if (kernel.shape[0] > data.shape[0] and
209
kernel.shape[1] > data.shape[1]):
210
kernel, data = data, kernel
211
elif (kernel.shape[0] <= data.shape[0] and
212
kernel.shape[1] <= data.shape[1]):
216
def correlate2d(data, kernel, output=None, mode="nearest", cval=0.0, fft=0):
217
"""correlate2d does 2d correlation of 'data' with 'kernel', storing
218
the result in 'output'.
220
supported 'mode's include:
221
'nearest' elements beyond boundary come from nearest edge pixel.
222
'wrap' elements beyond boundary come from the opposite array edge.
223
'reflect' elements beyond boundary come from reflection on same array edge.
224
'constant' elements beyond boundary are set to 'cval'
226
If fft is True, the correlation is performed using the FFT, else the
227
correlation is performed using the naive approach.
229
>>> a = num.arange(20*20)
230
>>> a = a.reshape((20,20))
231
>>> b = num.ones((5,5), dtype=num.float64)
232
>>> rn = correlate2d(a, b, fft=0)
233
>>> rf = correlate2d(a, b, fft=1)
234
>>> num.alltrue(num.ravel(rn-rf<1e-10))
237
data, kernel = _fix_data_kernel(data, kernel)
239
return _correlate2d_fft(data, kernel, output, mode, cval)
241
a = _correlate2d_naive(data, kernel, output, mode, cval)
245
def convolve2d(data, kernel, output=None, mode="nearest", cval=0.0, fft=0):
246
"""convolve2d does 2d convolution of 'data' with 'kernel', storing
247
the result in 'output'.
249
supported 'mode's include:
250
'nearest' elements beyond boundary come from nearest edge pixel.
251
'wrap' elements beyond boundary come from the opposite array edge.
252
'reflect' elements beyond boundary come from reflection on same array edge.
253
'constant' elements beyond boundary are set to 'cval'
255
>>> a = num.arange(20*20)
256
>>> a = a.reshape((20,20))
257
>>> b = num.ones((5,5), dtype=num.float64)
258
>>> rn = convolve2d(a, b, fft=0)
259
>>> rf = convolve2d(a, b, fft=1)
260
>>> num.alltrue(num.ravel(rn-rf<1e-10))
263
data, kernel = _fix_data_kernel(data, kernel)
264
kernel = kernel[::-1,::-1] # convolution -> correlation
266
return _correlate2d_fft(data, kernel, output, mode, cval)
268
return _correlate2d_naive(data, kernel, output, mode, cval)
270
def _boxcar(data, output, boxshape, mode, cval):
271
if len(boxshape) == 1:
272
_correlate.Boxcar2d(data[num.newaxis,...], 1, boxshape[0],
273
output[num.newaxis,...], mode, cval)
274
elif len(boxshape) == 2:
275
_correlate.Boxcar2d(data, boxshape[0], boxshape[1], output, mode, cval)
277
raise ValueError("boxshape must be a 1D or 2D shape.")
279
def boxcar(data, boxshape, output=None, mode="nearest", cval=0.0):
280
"""boxcar computes a 1D or 2D boxcar filter on every 1D or 2D subarray of data.
282
'boxshape' is a tuple of integers specifying the dimensions of the filter: e.g. (3,3)
284
if 'output' is specified, it should be the same shape as 'data' and
285
None will be returned.
287
supported 'mode's include:
288
'nearest' elements beyond boundary come from nearest edge pixel.
289
'wrap' elements beyond boundary come from the opposite array edge.
290
'reflect' elements beyond boundary come from reflection on same array edge.
291
'constant' elements beyond boundary are set to 'cval'
293
>>> boxcar(num.array([10, 0, 0, 0, 0, 0, 1000]), (3,), mode="nearest").astype(num.longlong)
294
array([ 6, 3, 0, 0, 0, 333, 666], dtype=int64)
295
>>> boxcar(num.array([10, 0, 0, 0, 0, 0, 1000]), (3,), mode="wrap").astype(num.longlong)
296
array([336, 3, 0, 0, 0, 333, 336], dtype=int64)
297
>>> boxcar(num.array([10, 0, 0, 0, 0, 0, 1000]), (3,), mode="reflect").astype(num.longlong)
298
array([ 6, 3, 0, 0, 0, 333, 666], dtype=int64)
299
>>> boxcar(num.array([10, 0, 0, 0, 0, 0, 1000]), (3,), mode="constant").astype(num.longlong)
300
array([ 3, 3, 0, 0, 0, 333, 333], dtype=int64)
301
>>> a = num.zeros((10,10))
305
>>> boxcar(a, (3,3)).astype(num.longlong)
306
array([[ 44, 22, 0, 0, 0, 0, 0, 0, 0, 0],
307
[ 22, 11, 0, 0, 0, 0, 0, 0, 0, 0],
308
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
309
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
310
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
311
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
312
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
313
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
314
[ 0, 0, 0, 0, 0, 0, 0, 0, 1111, 2222],
315
[ 0, 0, 0, 0, 0, 0, 0, 0, 2222, 4444]], dtype=int64)
316
>>> boxcar(a, (3,3), mode="wrap").astype(num.longlong)
317
array([[1122, 11, 0, 0, 0, 0, 0, 0, 1111, 1122],
318
[ 11, 11, 0, 0, 0, 0, 0, 0, 0, 11],
319
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
320
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
321
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
322
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
323
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
324
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
325
[1111, 0, 0, 0, 0, 0, 0, 0, 1111, 1111],
326
[1122, 11, 0, 0, 0, 0, 0, 0, 1111, 1122]], dtype=int64)
327
>>> boxcar(a, (3,3), mode="reflect").astype(num.longlong)
328
array([[ 44, 22, 0, 0, 0, 0, 0, 0, 0, 0],
329
[ 22, 11, 0, 0, 0, 0, 0, 0, 0, 0],
330
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
331
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
332
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
333
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
334
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
335
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
336
[ 0, 0, 0, 0, 0, 0, 0, 0, 1111, 2222],
337
[ 0, 0, 0, 0, 0, 0, 0, 0, 2222, 4444]], dtype=int64)
338
>>> boxcar(a, (3,3), mode="constant").astype(num.longlong)
339
array([[ 11, 11, 0, 0, 0, 0, 0, 0, 0, 0],
340
[ 11, 11, 0, 0, 0, 0, 0, 0, 0, 0],
341
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
342
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
343
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
344
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
345
[ 0, 0, 0, 0, 111, 111, 111, 0, 0, 0],
346
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
347
[ 0, 0, 0, 0, 0, 0, 0, 0, 1111, 1111],
348
[ 0, 0, 0, 0, 0, 0, 0, 0, 1111, 1111]], dtype=int64)
350
>>> a = num.zeros((10,10))
352
>>> boxcar(a, (3,3)).astype(num.longlong)
353
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
354
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
355
[ 0, 0, 12, 24, 37, 24, 12, 0, 0, 0],
356
[ 0, 0, 24, 49, 74, 49, 24, 0, 0, 0],
357
[ 0, 0, 37, 74, 111, 74, 37, 0, 0, 0],
358
[ 0, 0, 24, 49, 74, 49, 24, 0, 0, 0],
359
[ 0, 0, 12, 24, 37, 24, 12, 0, 0, 0],
360
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
361
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
362
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int64)
364
mode = pix_modes[ mode ]
366
woutput = data.astype(num.float64)
369
_fbroadcast(_boxcar, len(boxshape), data.shape,
370
(data, woutput), (boxshape, mode, cval))
374
def _fbroadcast(f, N, shape, args, params=()):
375
"""_fbroadcast(f, N, args, shape, params=()) calls 'f' for each of the
376
'N'-dimensional inner subnumarray of 'args'. Each subarray has
377
.shape == 'shape'[-N:]. There are a total of product(shape[:-N],axis=0)
381
apply(f, tuple(args)+params)
383
for i in range(shape[0]):
384
_fbroadcast(f, N, shape[1:], [x[i] for x in args], params)
387
import doctest, Convolve
388
return doctest.testmod(Convolve)
390
if __name__ == "__main__":