简体   繁体   English

ANSI C 代码中的一维线性卷积?

[英]1d linear convolution in ANSI C code?

Rather than reinvent the wheel, I wonder if anyone could refer me to a 1D linear convolution code snippet in ANSI C?与其重新发明轮子,我想知道是否有人可以向我推荐 ANSI C 中的一维线性卷积代码片段? I did a search on google and in stack overflow, but couldn't find anything in CI could use.我在谷歌和堆栈溢出中进行了搜索,但在 CI 中找不到任何可以使用的东西。

For example, for Arrays A, B, and C, all double-precision, where A and B are inputs and C is output, having lengths len_A , len_B , and len_C = len_A + len_B - 1 , respectively.例如,对于数组 A、B 和 C,都是双精度的,其中 A 和 B 是输入,C 是输出,长度分别为len_Alen_Blen_C = len_A + len_B - 1

My array sizes are small and so any speed increase in implementing fast convolution by FFT is not needed.我的数组大小很小,因此不需要通过 FFT 实现快速卷积的任何速度增加。 Looking for straightforward computation.寻找简单的计算。

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

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. 提示: 如果重新发明轮子所需的时间少于找到轮子的时间,请考虑前者。

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. 因为,我们正在对2个有限长度序列进行卷积,因此如果执行循环卷积而不是线性卷积,则实现期望的频率响应。 A very simple implementation of circular convolution will achieve the same result as the algorithm given by Alex. 循环卷积的一个非常简单的实现将获得与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)];
    }
}

I used @Mehrdad's approach, and created the following anwer: 我使用了@ Mehrdad的方法,并创建了以下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. 当第二个循环k变得大于n时,索引超出下限的问题,因此,猜测应该有额外的条件来防止这种情况。

This is my simple solution focused on readability这是我专注于可读性的简单解决方案

int lenh = 4;
double *h = new double[lenh] {2, 4, -1, 1 };
int lenx = 7;
double *x = new double[lenx] {-1, 2, 3, -2, 0, 1, 2 };
int leny = lenx + lenh - 1;
double *y = new double[leny];

for (int i = 0; i < leny; i++)
{
    y[i] = 0;                       // set to zero before sum
    for (int j = 0; j < lenh; j++)
    {
        if (i - j >= 0 && i - j < lenx)
        {
            y[i] += x[i - j] * h[j];    // convolve: multiply and accumulate
        }
        
    }
    std::cout << y[i] << std::endl;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM