File indexing completed on 2024-04-28 04:51:59

0001 /*
0002 SPDX-FileCopyrightText: 2012 Simon A. Eugster (Granjow)  <simon.eu@gmail.com>
0003 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "fftCorrelation.h"
0007 #include <QElapsedTimer>
0008 extern "C" {
0009 #include "../external/kiss_fft/tools/kiss_fftr.h"
0010 }
0011 
0012 #include "kdenlive_debug.h"
0013 #include <algorithm>
0014 #include <vector>
0015 
0016 void FFTCorrelation::correlate(const qint64 *left, const size_t leftSize, const qint64 *right, const size_t rightSize, qint64 *out_correlated)
0017 {
0018     auto *correlatedFloat = new float[leftSize + rightSize + 1];
0019     correlate(left, leftSize, right, rightSize, correlatedFloat);
0020 
0021     // The correlation vector will have entries up to N (number of entries
0022     // of the vector), so converting to integers will not lose that much
0023     // of precision.
0024     for (size_t i = 0; i < leftSize + rightSize + 1; ++i) {
0025         out_correlated[i] = qint64(correlatedFloat[i]);
0026     }
0027     delete[] correlatedFloat;
0028 }
0029 
0030 void FFTCorrelation::correlate(const qint64 *left, const size_t leftSize, const qint64 *right, const size_t rightSize, float *out_correlated)
0031 {
0032     QElapsedTimer t;
0033     t.start();
0034 
0035     auto *leftF = new float[leftSize];
0036     auto *rightF = new float[rightSize];
0037 
0038     // First the qint64 values need to be normalized to floats
0039     // Dividing by the max value is maybe not the best solution, but the
0040     // maximum value after correlation should not be larger than the longest
0041     // vector since each value should be at most 1
0042     qint64 maxLeft = 1;
0043     qint64 maxRight = 1;
0044     for (size_t i = 0; i < leftSize; ++i) {
0045         if (qAbs(left[i]) > maxLeft) {
0046             maxLeft = qAbs(left[i]);
0047         }
0048     }
0049     for (size_t i = 0; i < rightSize; ++i) {
0050         if (qAbs(right[i]) > maxRight) {
0051             maxRight = qAbs(right[i]);
0052         }
0053     }
0054 
0055     // One side needs to be reversed, since multiplication in frequency domain (fourier space)
0056     // calculates the convolution: \sum l[x]r[N-x] and not the correlation: \sum l[x]r[x]
0057     for (size_t i = 0; i < leftSize; ++i) {
0058         leftF[i] = float(left[i]) / maxLeft;
0059     }
0060     for (size_t i = 0; i < rightSize; ++i) {
0061         rightF[rightSize - 1 - i] = float(right[i]) / maxRight;
0062     }
0063 
0064     // Now we can convolve to get the correlation
0065     convolve(leftF, leftSize, rightF, rightSize, out_correlated);
0066 
0067     qCDebug(KDENLIVE_LOG) << "Correlation (FFT based) computed in " << t.elapsed() << " ms.";
0068     delete[] leftF;
0069     delete[] rightF;
0070 }
0071 
0072 void FFTCorrelation::convolve(const float *left, const size_t leftSize, const float *right, const size_t rightSize, float *out_convolved)
0073 {
0074     QElapsedTimer time;
0075     time.start();
0076 
0077     // To avoid issues with repetition (we are dealing with cosine waves
0078     // in the fourier domain) we need to pad the vectors to at least twice their size,
0079     // otherwise convolution would convolve with the repeated pattern as well
0080     size_t largestSize = std::max(leftSize, rightSize);
0081 
0082     // The vectors must have the same size (same frequency resolution!) and should
0083     // be a power of 2 (for FFT).
0084     size_t size = 64;
0085     while (size / 2 < largestSize) {
0086         size = size << 1;
0087     }
0088 
0089     const size_t fft_size = size / 2 + 1;
0090     kiss_fftr_cfg fftConfig = kiss_fftr_alloc(int(size), 0, nullptr, nullptr);
0091     kiss_fftr_cfg ifftConfig = kiss_fftr_alloc(int(size), 1, nullptr, nullptr);
0092     std::vector<kiss_fft_cpx> leftFFT(fft_size);
0093     std::vector<kiss_fft_cpx> rightFFT(fft_size);
0094     std::vector<kiss_fft_cpx> correlatedFFT(fft_size);
0095 
0096     // Fill in the data into our new vectors with padding
0097     std::vector<float> leftData(size, 0);
0098     std::vector<float> rightData(size, 0);
0099     std::vector<float> convolved(size);
0100 
0101     std::copy(left, left + leftSize, leftData.begin());
0102     std::copy(right, right + rightSize, rightData.begin());
0103 
0104     // Fourier transformation of the vectors
0105     kiss_fftr(fftConfig, &leftData[0], &leftFFT[0]);
0106     kiss_fftr(fftConfig, &rightData[0], &rightFFT[0]);
0107 
0108     // Convolution in spacial domain is a multiplication in fourier domain. O(n).
0109     for (size_t i = 0; i < correlatedFFT.size(); ++i) {
0110         correlatedFFT[i].r = leftFFT[i].r * rightFFT[i].r - leftFFT[i].i * rightFFT[i].i;
0111         correlatedFFT[i].i = leftFFT[i].r * rightFFT[i].i + leftFFT[i].i * rightFFT[i].r;
0112     }
0113 
0114     // Inverse fourier transformation to get the convolved data.
0115     // Insert one element at the beginning to obtain the same result
0116     // that we also get with the nested for loop correlation.
0117     *out_convolved = 0;
0118     size_t out_size = leftSize + rightSize + 1;
0119 
0120     kiss_fftri(ifftConfig, &correlatedFFT[0], &convolved[0]);
0121     std::copy(convolved.begin(), convolved.begin() + int(out_size) - 1, out_convolved + 1);
0122 
0123     // Finally some cleanup.
0124     kiss_fftr_free(fftConfig);
0125     kiss_fftr_free(ifftConfig);
0126 
0127     qCDebug(KDENLIVE_LOG) << "FFT convolution computed. Time taken: " << time.elapsed() << " ms";
0128 }