Repository: openmrslab/suspect

Issue:
Spline denoising fails on numpy 1.12
In numpy 1.12 spline denoising fails because it passes floating point values to _pad and roll instead of integer values.

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/9657280/badge)](https://coveralls.io/builds/9657280)

Coverage increased (+1.6%) to 74.955% when pulling **3e435af1f6fc1029fab1a96315317b0dd8699ab8 on #39_denoising** into **b03275c0dd645fd522cab182a2cfbe28dfc948ed on master**.


Patch:
diff --git a/suspect/processing/denoising.py b/suspect/processing/denoising.py
index f55b7ae..46dfc04 100644
--- a/suspect/processing/denoising.py
+++ b/suspect/processing/denoising.py
@@ -85,8 +85,8 @@ def svd(input_signal, rank):
 
 def spline(input_signal, num_splines, spline_order):
     # input signal  has to be a multiple of num_splines
-    padded_input_signal = _pad(input_signal, (numpy.ceil(len(input_signal) / float(num_splines))) * num_splines)
-    stride = len(padded_input_signal) / num_splines
+    padded_input_signal = _pad(input_signal, int(numpy.ceil(len(input_signal) / float(num_splines))) * num_splines)
+    stride = len(padded_input_signal) // num_splines
     import scipy.signal
     # we construct the spline basis by building the first one, then the rest
     # are identical copies offset by stride
@@ -95,11 +95,11 @@ def spline(input_signal, num_splines, spline_order):
     spline_basis = numpy.zeros((num_splines + 1, len(padded_input_signal)), input_signal.dtype)
     for i in range(num_splines + 1):
         spline_basis[i, :] = numpy.roll(first_spline, i * stride)
-    spline_basis[:(num_splines / 4), (len(padded_input_signal) / 2):] = 0.0
-    spline_basis[(num_splines * 3 / 4):, :(len(padded_input_signal) / 2)] = 0.0
+    spline_basis[:(num_splines // 4), (len(padded_input_signal) // 2):] = 0.0
+    spline_basis[(num_splines * 3 // 4):, :(len(padded_input_signal) // 2)] = 0.0
     coefficients = numpy.linalg.lstsq(spline_basis.T, padded_input_signal)
     recon = numpy.dot(coefficients[0], spline_basis)
-    start_offset = (len(padded_input_signal) - len(input_signal)) / 2.0
+    start_offset = (len(padded_input_signal) - len(input_signal)) // 2
     return recon[start_offset:(start_offset + len(input_signal))]
 
 
@@ -108,9 +108,9 @@ def wavelet(input_signal, wavelet_shape, threshold):
     # we have to pad the signal to make it a power of two
     next_power_of_two = int(numpy.floor(numpy.log2(len(input_signal))) + 1)
     padded_input_signal = _pad(input_signal, 2**next_power_of_two)
-    wt_coeffs = pywt.wavedec(padded_input_signal, wavelet_shape, level=None, mode='per')
+    wt_coeffs = pywt.wavedec(padded_input_signal, wavelet_shape, level=None, mode='periodization')
     denoised_coeffs = wt_coeffs[:]
     denoised_coeffs[1:] = (pywt.threshold(i, value=threshold) for i in denoised_coeffs[1:])
-    recon = pywt.waverec(denoised_coeffs, wavelet_shape, mode='per')
-    start_offset = (len(padded_input_signal) - len(input_signal)) / 2.0
+    recon = pywt.waverec(denoised_coeffs, wavelet_shape, mode='periodization')
+    start_offset = (len(padded_input_signal) - len(input_signal)) // 2
     return recon[start_offset:(start_offset + len(input_signal))]


Test patch:
diff --git a/tests/test_mrs/test_processing/test_denoising.py b/tests/test_mrs/test_processing/test_denoising.py
new file mode 100644
index 0000000..2e45982
--- /dev/null
+++ b/tests/test_mrs/test_processing/test_denoising.py
@@ -0,0 +1,29 @@
+import suspect
+
+import numpy
+
+import warnings
+warnings.filterwarnings("ignore", message="numpy.dtype size changed")
+
+def test_spline():
+    # we need to check if this runs correctly when number of splines is not a
+    # factor of length of signal, so that padding is required.
+    # generate a sample signal
+    input_signal = numpy.random.randn(295) + 10
+    # denoise the signal with splines
+    output_signal = suspect.processing.denoising.spline(input_signal, 32, 2)
+    # main thing is that the test runs without errors, but we can also check
+    # for reduced std in the result
+    assert numpy.std(output_signal) < numpy.std(input_signal)
+
+
+def test_wavelet():
+    # this is to check if the code runs without throwing double -> integer
+    # conversion issues
+    # generate a sample signal
+    input_signal = numpy.random.randn(295) + 10
+    # denoise the signal with splines
+    output_signal = suspect.processing.denoising.wavelet(input_signal, "db8", 1e-2)
+    # main thing is that the test runs without errors, but we can also check
+    # for reduced std in the result
+    assert numpy.std(output_signal) < numpy.std(input_signal)
\ No newline at end of file