@@ -165,7 +165,8 @@ def correlate(in1, in2, mode='full', method='auto'):
165165
166166 z[...,k,...] = sum[..., i_l, ...] x[..., i_l,...] * conj(y[..., i_l - k,...])
167167
168- This way, if x and y are 1-D arrays and ``z = correlate(x, y, 'full')`` then
168+ This way, if x and y are 1-D arrays and ``z = correlate(x, y, 'full')``
169+ then
169170
170171 .. math::
171172
@@ -227,46 +228,51 @@ def correlate(in1, in2, mode='full', method='auto'):
227228 if method in ('fft' , 'auto' ):
228229 return convolve (in1 , _reverse_and_conj (in2 ), mode , method )
229230
230- # fastpath to faster numpy.correlate for 1d inputs when possible
231- if _np_conv_ok (in1 , in2 , mode ):
232- return np .correlate (in1 , in2 , mode )
231+ elif method == 'direct' :
232+ # fastpath to faster numpy.correlate for 1d inputs when possible
233+ if _np_conv_ok (in1 , in2 , mode ):
234+ return np .correlate (in1 , in2 , mode )
233235
234- # _correlateND is far slower when in2.size > in1.size, so swap them
235- # and then undo the effect afterward if mode == 'full'. Also, it fails
236- # with 'valid' mode if in2 is larger than in1, so swap those, too.
237- # Don't swap inputs for 'same' mode, since shape of in1 matters.
238- swapped_inputs = ((mode == 'full' ) and (in2 .size > in1 .size ) or
239- _inputs_swap_needed (mode , in1 .shape , in2 .shape ))
236+ # _correlateND is far slower when in2.size > in1.size, so swap them
237+ # and then undo the effect afterward if mode == 'full'. Also, it fails
238+ # with 'valid' mode if in2 is larger than in1, so swap those, too.
239+ # Don't swap inputs for 'same' mode, since shape of in1 matters.
240+ swapped_inputs = ((mode == 'full' ) and (in2 .size > in1 .size ) or
241+ _inputs_swap_needed (mode , in1 .shape , in2 .shape ))
240242
241- if swapped_inputs :
242- in1 , in2 = in2 , in1
243+ if swapped_inputs :
244+ in1 , in2 = in2 , in1
243245
244- if mode == 'valid' :
245- ps = [i - j + 1 for i , j in zip (in1 .shape , in2 .shape )]
246- out = np .empty (ps , in1 .dtype )
246+ if mode == 'valid' :
247+ ps = [i - j + 1 for i , j in zip (in1 .shape , in2 .shape )]
248+ out = np .empty (ps , in1 .dtype )
247249
248- z = sigtools ._correlateND (in1 , in2 , out , val )
250+ z = sigtools ._correlateND (in1 , in2 , out , val )
249251
250- else :
251- ps = [i + j - 1 for i , j in zip (in1 .shape , in2 .shape )]
252+ else :
253+ ps = [i + j - 1 for i , j in zip (in1 .shape , in2 .shape )]
252254
253- # zero pad input
254- in1zpadded = np .zeros (ps , in1 .dtype )
255- sc = [slice (0 , i ) for i in in1 .shape ]
256- in1zpadded [sc ] = in1 .copy ()
255+ # zero pad input
256+ in1zpadded = np .zeros (ps , in1 .dtype )
257+ sc = [slice (0 , i ) for i in in1 .shape ]
258+ in1zpadded [sc ] = in1 .copy ()
257259
258- if mode == 'full' :
259- out = np .empty (ps , in1 .dtype )
260- elif mode == 'same' :
261- out = np .empty (in1 .shape , in1 .dtype )
260+ if mode == 'full' :
261+ out = np .empty (ps , in1 .dtype )
262+ elif mode == 'same' :
263+ out = np .empty (in1 .shape , in1 .dtype )
262264
263- z = sigtools ._correlateND (in1zpadded , in2 , out , val )
265+ z = sigtools ._correlateND (in1zpadded , in2 , out , val )
264266
265- if swapped_inputs :
266- # Reverse and conjugate to undo the effect of swapping inputs
267- z = _reverse_and_conj (z )
267+ if swapped_inputs :
268+ # Reverse and conjugate to undo the effect of swapping inputs
269+ z = _reverse_and_conj (z )
270+
271+ return z
268272
269- return z
273+ else :
274+ raise ValueError ("Acceptable method flags are 'auto',"
275+ " 'direct', or 'fft'." )
270276
271277
272278def _centered (arr , newshape ):
@@ -298,8 +304,6 @@ def fftconvolve(in1, in2, mode="full"):
298304 First input.
299305 in2 : array_like
300306 Second input. Should have the same number of dimensions as `in1`.
301- If operating in 'valid' mode, either `in1` or `in2` must be
302- at least as large as the other in every dimension.
303307 mode : str {'full', 'valid', 'same'}, optional
304308 A string indicating the size of the output:
305309
@@ -308,7 +312,8 @@ def fftconvolve(in1, in2, mode="full"):
308312 of the inputs. (Default)
309313 ``valid``
310314 The output consists only of those elements that do not
311- rely on the zero-padding.
315+ rely on the zero-padding. In 'valid' mode, either `in1` or `in2`
316+ must be at least as large as the other in every dimension.
312317 ``same``
313318 The output is the same size as `in1`, centered
314319 with respect to the 'full' output.
@@ -474,7 +479,8 @@ def _fftconv_faster(x, h, mode):
474479 out_shape = [n - k + 1 for n , k in zip (x .shape , h .shape )]
475480 big_O_constant = 41954.28006344 if x .ndim == 1 else 66453.24316434
476481 else :
477- raise ValueError ('mode is invalid' )
482+ raise ValueError ("Acceptable mode flags are 'valid',"
483+ " 'same', or 'full'." )
478484
479485 # see whether the Fourier transform convolution method or the direct
480486 # convolution method is faster (discussed in scikit-image PR #1792)
@@ -497,9 +503,16 @@ def _np_conv_ok(volume, kernel, mode):
497503 See if numpy supports convolution of `volume` and `kernel` (i.e. both are
498504 1D ndarrays and of the appropriate shape). Numpy's 'same' mode uses the
499505 size of the larger input, while Scipy's uses the size of the first input.
506+
507+ Invalid mode strings will return False and be caught by the calling func.
500508 """
501- np_conv_ok = volume .ndim == kernel .ndim == 1
502- return np_conv_ok and (volume .size >= kernel .size or mode != 'same' )
509+ if volume .ndim == kernel .ndim == 1 :
510+ if mode in ('full' , 'valid' ):
511+ return True
512+ elif mode == 'same' :
513+ return volume .size >= kernel .size
514+ else :
515+ return False
503516
504517
505518def _timeit_fast (stmt = "pass" , setup = "pass" , repeat = 3 ):
@@ -755,6 +768,9 @@ def convolve(in1, in2, mode='full', method='auto'):
755768
756769 if volume .ndim == kernel .ndim == 0 :
757770 return volume * kernel
771+ elif volume .ndim != kernel .ndim :
772+ raise ValueError ("volume and kernel should have the same "
773+ "dimensionality" )
758774
759775 if _inputs_swap_needed (mode , volume .shape , kernel .shape ):
760776 # Convolution is commutative; order doesn't have any effect on output
@@ -769,12 +785,15 @@ def convolve(in1, in2, mode='full', method='auto'):
769785 if result_type .kind in {'u' , 'i' }:
770786 out = np .around (out )
771787 return out .astype (result_type )
788+ elif method == 'direct' :
789+ # fastpath to faster numpy.convolve for 1d inputs when possible
790+ if _np_conv_ok (volume , kernel , mode ):
791+ return np .convolve (volume , kernel , mode )
772792
773- # fastpath to faster numpy.convolve for 1d inputs when possible
774- if _np_conv_ok (volume , kernel , mode ):
775- return np .convolve (volume , kernel , mode )
776-
777- return correlate (volume , _reverse_and_conj (kernel ), mode , 'direct' )
793+ return correlate (volume , _reverse_and_conj (kernel ), mode , 'direct' )
794+ else :
795+ raise ValueError ("Acceptable method flags are 'auto',"
796+ " 'direct', or 'fft'." )
778797
779798
780799def order_filter (a , domain , rank ):
@@ -945,8 +964,6 @@ def convolve2d(in1, in2, mode='full', boundary='fill', fillvalue=0):
945964 First input.
946965 in2 : array_like
947966 Second input. Should have the same number of dimensions as `in1`.
948- If operating in 'valid' mode, either `in1` or `in2` must be
949- at least as large as the other in every dimension.
950967 mode : str {'full', 'valid', 'same'}, optional
951968 A string indicating the size of the output:
952969
@@ -955,11 +972,11 @@ def convolve2d(in1, in2, mode='full', boundary='fill', fillvalue=0):
955972 of the inputs. (Default)
956973 ``valid``
957974 The output consists only of those elements that do not
958- rely on the zero-padding.
975+ rely on the zero-padding. In 'valid' mode, either `in1` or `in2`
976+ must be at least as large as the other in every dimension.
959977 ``same``
960978 The output is the same size as `in1`, centered
961979 with respect to the 'full' output.
962-
963980 boundary : str {'fill', 'wrap', 'symm'}, optional
964981 A flag indicating how to handle boundaries:
965982
@@ -1036,8 +1053,6 @@ def correlate2d(in1, in2, mode='full', boundary='fill', fillvalue=0):
10361053 First input.
10371054 in2 : array_like
10381055 Second input. Should have the same number of dimensions as `in1`.
1039- If operating in 'valid' mode, either `in1` or `in2` must be
1040- at least as large as the other in every dimension.
10411056 mode : str {'full', 'valid', 'same'}, optional
10421057 A string indicating the size of the output:
10431058
@@ -1046,11 +1061,11 @@ def correlate2d(in1, in2, mode='full', boundary='fill', fillvalue=0):
10461061 of the inputs. (Default)
10471062 ``valid``
10481063 The output consists only of those elements that do not
1049- rely on the zero-padding.
1064+ rely on the zero-padding. In 'valid' mode, either `in1` or `in2`
1065+ must be at least as large as the other in every dimension.
10501066 ``same``
10511067 The output is the same size as `in1`, centered
10521068 with respect to the 'full' output.
1053-
10541069 boundary : str {'fill', 'wrap', 'symm'}, optional
10551070 A flag indicating how to handle boundaries:
10561071
0 commit comments