// Copyright ©2018 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package fourier_test import ( "fmt" "math" "math/cmplx" "gonum.org/v1/gonum/dsp/fourier" "gonum.org/v1/gonum/floats/scalar" "gonum.org/v1/gonum/mat" ) func ExampleFFT_Coefficients() { // Period is a set of samples over a given period. period := []float64{1, 0, 2, 0, 4, 0, 2, 0} // Initialize an FFT and perform the analysis. fft := fourier.NewFFT(len(period)) coeff := fft.Coefficients(nil, period) for i, c := range coeff { fmt.Printf("freq=%v cycles/period, magnitude=%v, phase=%.4g\n", fft.Freq(i), cmplx.Abs(c), cmplx.Phase(c)) } // Output: // // freq=0 cycles/period, magnitude=9, phase=0 // freq=0.125 cycles/period, magnitude=3, phase=3.142 // freq=0.25 cycles/period, magnitude=1, phase=-0 // freq=0.375 cycles/period, magnitude=3, phase=3.142 // freq=0.5 cycles/period, magnitude=9, phase=0 } func ExampleFFT_Coefficients_tone() { // Tone is a set of samples over a second of a pure Middle C. const ( mC = 261.625565 // Hz samples = 44100 ) tone := make([]float64, samples) for i := range tone { tone[i] = math.Sin(mC * 2 * math.Pi * float64(i) / samples) } // Initialize an FFT and perform the analysis. fft := fourier.NewFFT(samples) coeff := fft.Coefficients(nil, tone) var maxFreq, magnitude, mean float64 for i, c := range coeff { m := cmplx.Abs(c) mean += m if m > magnitude { magnitude = m maxFreq = fft.Freq(i) * samples } } fmt.Printf("freq=%v Hz, magnitude=%.0f, mean=%.4f\n", maxFreq, magnitude, mean/samples) // Output: // // freq=262 Hz, magnitude=17296, mean=2.7835 } func ExampleCmplxFFT_Coefficients() { // Period is a set of samples over a given period. period := []complex128{1, 0, 2, 0, 4, 0, 2, 0} // Initialize a complex FFT and perform the analysis. fft := fourier.NewCmplxFFT(len(period)) coeff := fft.Coefficients(nil, period) for i := range coeff { // Center the spectrum. i = fft.ShiftIdx(i) fmt.Printf("freq=%v cycles/period, magnitude=%v, phase=%.4g\n", fft.Freq(i), cmplx.Abs(coeff[i]), cmplx.Phase(coeff[i])) } // Output: // // freq=-0.5 cycles/period, magnitude=9, phase=0 // freq=-0.375 cycles/period, magnitude=3, phase=3.142 // freq=-0.25 cycles/period, magnitude=1, phase=0 // freq=-0.125 cycles/period, magnitude=3, phase=3.142 // freq=0 cycles/period, magnitude=9, phase=0 // freq=0.125 cycles/period, magnitude=3, phase=3.142 // freq=0.25 cycles/period, magnitude=1, phase=0 // freq=0.375 cycles/period, magnitude=3, phase=3.142 } func Example_fFT2() { // This example shows how to perform a 2D fourier transform // on an image. The transform identifies the lines present // in the image. // Image is a set of diagonal lines. image := mat.NewDense(11, 11, []float64{ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, }) // Make appropriately sized real and complex FFT types. r, c := image.Dims() fft := fourier.NewFFT(c) cfft := fourier.NewCmplxFFT(r) // Only c/2+1 coefficients will be returned for // the real FFT. c = c/2 + 1 // Perform the first axis transform. rows := make([]complex128, r*c) for i := 0; i < r; i++ { fft.Coefficients(rows[c*i:c*(i+1)], image.RawRowView(i)) } // Perform the second axis transform, storing // the result in freqs. freqs := mat.NewDense(c, c, nil) column := make([]complex128, r) for j := 0; j < c; j++ { for i := 0; i < r; i++ { column[i] = rows[i*c+j] } cfft.Coefficients(column, column) for i, v := range column[:c] { freqs.Set(i, j, scalar.Round(cmplx.Abs(v), 1)) } } fmt.Printf("%v\n", mat.Formatted(freqs)) // Output: // // ⎡ 40 0.4 0.5 1.4 3.2 1.1⎤ // ⎢ 0.4 0.5 0.7 1.8 4 1.2⎥ // ⎢ 0.5 0.7 1.1 2.8 5.9 1.7⎥ // ⎢ 1.4 1.8 2.8 6.8 14.1 3.8⎥ // ⎢ 3.2 4 5.9 14.1 27.5 6.8⎥ // ⎣ 1.1 1.2 1.7 3.8 6.8 1.6⎦ } func Example_cmplxFFT2() { // Image is a set of diagonal lines. image := mat.NewDense(11, 11, []float64{ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, }) // Make appropriately sized complex FFT. // Rows and columns are the same, so the same // CmplxFFT can be used for both axes. r, c := image.Dims() cfft := fourier.NewCmplxFFT(r) // Perform the first axis transform. rows := make([]complex128, r*c) for i := 0; i < r; i++ { row := rows[c*i : c*(i+1)] for j, v := range image.RawRowView(i) { row[j] = complex(v, 0) } cfft.Coefficients(row, row) } // Perform the second axis transform, storing // the result in freqs. freqs := mat.NewDense(c, c, nil) column := make([]complex128, r) for j := 0; j < c; j++ { for i := 0; i < r; i++ { column[i] = rows[i*c+j] } cfft.Coefficients(column, column) for i, v := range column { // Center the frequencies. freqs.Set(cfft.UnshiftIdx(i), cfft.UnshiftIdx(j), scalar.Round(cmplx.Abs(v), 1)) } } fmt.Printf("%v\n", mat.Formatted(freqs)) // Output: // // ⎡ 1.6 6.8 3.8 1.7 1.2 1.1 1.1 1.4 2.6 3.9 1.1⎤ // ⎢ 6.8 27.5 14.1 5.9 4 3.2 3 3 3.9 3.2 3.9⎥ // ⎢ 3.8 14.1 6.8 2.8 1.8 1.4 1.2 1.1 1.4 3.9 2.6⎥ // ⎢ 1.7 5.9 2.8 1.1 0.7 0.5 0.5 0.5 1.1 3 1.4⎥ // ⎢ 1.2 4 1.8 0.7 0.5 0.4 0.4 0.5 1.2 3 1.1⎥ // ⎢ 1.1 3.2 1.4 0.5 0.4 40 0.4 0.5 1.4 3.2 1.1⎥ // ⎢ 1.1 3 1.2 0.5 0.4 0.4 0.5 0.7 1.8 4 1.2⎥ // ⎢ 1.4 3 1.1 0.5 0.5 0.5 0.7 1.1 2.8 5.9 1.7⎥ // ⎢ 2.6 3.9 1.4 1.1 1.2 1.4 1.8 2.8 6.8 14.1 3.8⎥ // ⎢ 3.9 3.2 3.9 3 3 3.2 4 5.9 14.1 27.5 6.8⎥ // ⎣ 1.1 3.9 2.6 1.4 1.1 1.1 1.2 1.7 3.8 6.8 1.6⎦ }