1d linear convolution in ANSI C code?

前端 未结 4 1908
孤城傲影
孤城傲影 2021-02-01 08:49

Rather than reinvent the wheel, I wonder if anyone could refer me to a 1D linear convolution code snippet in ANSI C? I did a search on google and in stack overflow, but couldn\'

相关标签:
4条回答
  • 2021-02-01 09:31

    Here's how:

    #include <stddef.h>
    #include <stdio.h>
    
    void convolve(const double Signal[/* SignalLen */], size_t SignalLen,
                  const double Kernel[/* KernelLen */], size_t KernelLen,
                  double Result[/* SignalLen + KernelLen - 1 */])
    {
      size_t n;
    
      for (n = 0; n < SignalLen + KernelLen - 1; n++)
      {
        size_t kmin, kmax, k;
    
        Result[n] = 0;
    
        kmin = (n >= KernelLen - 1) ? n - (KernelLen - 1) : 0;
        kmax = (n < SignalLen - 1) ? n : SignalLen - 1;
    
        for (k = kmin; k <= kmax; k++)
        {
          Result[n] += Signal[k] * Kernel[n - k];
        }
      }
    }
    
    void printSignal(const char* Name,
                     double Signal[/* SignalLen */], size_t SignalLen)
    {
      size_t i;
    
      for (i = 0; i < SignalLen; i++)
      {
        printf("%s[%zu] = %f\n", Name, i, Signal[i]);
      }
      printf("\n");
    }
    
    #define ELEMENT_COUNT(X) (sizeof(X) / sizeof((X)[0]))
    
    int main(void)
    {
      double signal[] = { 1, 1, 1, 1, 1 };
      double kernel[] = { 1, 1, 1, 1, 1 };
      double result[ELEMENT_COUNT(signal) + ELEMENT_COUNT(kernel) - 1];
    
      convolve(signal, ELEMENT_COUNT(signal),
               kernel, ELEMENT_COUNT(kernel),
               result);
    
      printSignal("signal", signal, ELEMENT_COUNT(signal));
      printSignal("kernel", kernel, ELEMENT_COUNT(kernel));
      printSignal("result", result, ELEMENT_COUNT(result));
    
      return 0;
    }
    

    Output:

    signal[0] = 1.000000
    signal[1] = 1.000000
    signal[2] = 1.000000
    signal[3] = 1.000000
    signal[4] = 1.000000
    
    kernel[0] = 1.000000
    kernel[1] = 1.000000
    kernel[2] = 1.000000
    kernel[3] = 1.000000
    kernel[4] = 1.000000
    
    result[0] = 1.000000
    result[1] = 2.000000
    result[2] = 3.000000
    result[3] = 4.000000
    result[4] = 5.000000
    result[5] = 4.000000
    result[6] = 3.000000
    result[7] = 2.000000
    result[8] = 1.000000
    
    0 讨论(0)
  • 2021-02-01 09:32

    Since, we are taking convolution of 2 finite length sequences, hence the desired frequency response is achieved if circular convolution is performed rather than linear convolution. A very simple implementation of circular convolution will achieve the same result as the algorithm given by Alex.

    #define MOD(n, N) ((n<0)? N+n : n)
    ......
    ......
    
    for(n=0; n < signal_Length + Kernel_Length - 1; n++)
    {
        out[n] = 0;
        for(m=0; m < Kernel_Length; m++)
        {
            out[n] = h[m] * x[MOD(n-m, N)];
        }
    }
    
    0 讨论(0)
  • 2021-02-01 09:39

    I used @Mehrdad's approach, and created the following anwer:

    void conv(const double v1[], size_t n1, const double v2[], size_t n2, double r[])
    {
        for (size_t n = 0; n < n1 + n2 - 1; n++)
            for (size_t k = 0; k < max(n1, n2) && n >= k; k++)
                r[n] += (k < n1 ? v1[k] : 0) * (n - k < n2 ? v2[n - k] : 0);
    }
    

    There's problem with index exceeding lower bound when in second loops k gets bigger than n, so, guess there should be extra condition to prevent that.

    0 讨论(0)
  • 2021-02-01 09:48

    Not tested, but it seems like it would work...

    void conv(const double v1[], size_t n1, const double v2[], size_t n2, double r[])
    {
        for (size_t n = 0; n < n1 + n2 - 1; n++)
            for (size_t k = 0; k < max(n1, n2); k++)
                r[n] += (k < n1 ? v1[k] : 0) * (n - k < n2 ? v2[n - k] : 0);
    }
    

    Tip: If it takes less time to reinvent a wheel than to find one, do consider the former.

    0 讨论(0)
提交回复
热议问题