// CorrBankFFT computes the correlation of an image with a bank of filters. // h_p[u, v] = (f corr g_p)[u, v] func CorrBankFFT(f *rimg64.Image, g *Bank) (*rimg64.Multi, error) { out := ValidSize(f.Size(), g.Size()) if out.X <= 0 || out.Y <= 0 { return nil, nil } // Determine optimal size for FFT. work, _ := FFT2Size(f.Size()) // Re-use FFT of image. fhat := fftw.NewArray2(work.X, work.Y) copyImageTo(fhat, f) fftw.FFT2To(fhat, fhat) // Transform of each filter. curr := fftw.NewArray2(work.X, work.Y) fwd := fftw.NewPlan2(curr, curr, fftw.Forward, fftw.Estimate) defer fwd.Destroy() bwd := fftw.NewPlan2(curr, curr, fftw.Backward, fftw.Estimate) defer bwd.Destroy() h := rimg64.NewMulti(out.X, out.Y, len(g.Filters)) alpha := complex(1/float64(work.X*work.Y), 0) // For each output channel. for p, gp := range g.Filters { // Take FFT. copyImageTo(curr, gp) fwd.Execute() // h_p[x] = (G_p corr F)[x] // H_p[x] = conj(G_p[x]) F[x] scaleMul(curr, alpha, curr, fhat) bwd.Execute() copyRealToChannel(h, p, curr) } return h, nil }
// CorrFFT computes the correlation of an image with a filter. // h[u, v] = (f corr g)[u, v] func CorrFFT(f, g *rimg64.Image) (*rimg64.Image, error) { out := ValidSize(f.Size(), g.Size()) if out.X <= 0 || out.Y <= 0 { return nil, nil } // Determine optimal size for FFT. work, _ := FFT2Size(f.Size()) fhat := fftw.NewArray2(work.X, work.Y) ghat := fftw.NewArray2(work.X, work.Y) // Take forward transforms. copyImageTo(fhat, f) fftw.FFT2To(fhat, fhat) copyImageTo(ghat, g) fftw.FFT2To(ghat, ghat) // Scale such that convolution theorem holds. n := float64(work.X * work.Y) scaleMul(fhat, complex(1/n, 0), ghat, fhat) // Take inverse transform. h := rimg64.New(out.X, out.Y) fftw.IFFT2To(fhat, fhat) copyRealTo(h, fhat) return h, nil }
// CorrMultiBankFFT computes the correlation of // a multi-channel image with a bank of multi-channel filters. // h_p[u, v] = sum_q (f_q corr g_pq)[u, v] func CorrMultiBankFFT(f *rimg64.Multi, g *MultiBank) (*rimg64.Multi, error) { out := ValidSize(f.Size(), g.Size()) if out.X <= 0 || out.Y <= 0 { return nil, nil } // Determine optimal size for FFT. work, _ := FFT2Size(f.Size()) // Cache FFT of each channel of image. fhat := make([]*fftw.Array2, f.Channels) for i := range fhat { fhat[i] = fftw.NewArray2(work.X, work.Y) copyChannelTo(fhat[i], f, i) fftw.FFT2To(fhat[i], fhat[i]) } curr := fftw.NewArray2(work.X, work.Y) fwd := fftw.NewPlan2(curr, curr, fftw.Forward, fftw.Estimate) defer fwd.Destroy() sum := fftw.NewArray2(work.X, work.Y) bwd := fftw.NewPlan2(sum, sum, fftw.Backward, fftw.Estimate) defer bwd.Destroy() h := rimg64.NewMulti(out.X, out.Y, len(g.Filters)) alpha := complex(1/float64(work.X*work.Y), 0) // For each output channel. for p, gp := range g.Filters { zero(sum) // For each input channel. for q := 0; q < f.Channels; q++ { // Take FFT of this input channel. copyChannelTo(curr, gp, q) fwd.Execute() // h_p[x] = (G_qp corr F_p)[x] // H_p[x] = conj(G_qp[x]) F_p[x] addScaleMul(sum, alpha, curr, fhat[q]) } bwd.Execute() copyRealToChannel(h, p, sum) } return h, nil }