英文:
Filtering out short fluctuations in binary array from digital input
问题
我有一个由0/1值表示的TTL信号数组。我想要平滑掉任何持续时间非常短暂(例如,少于4个样本)的数值变化,我知道这些变化是噪音。
例如,如果4是最小合法脉冲长度:
[0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1]
应该变成
[0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1]
似乎很难提出一个确切的替换规则,尽管大多数情况在主观上都是明显的。总的来说,只要短暂的波动没有被保留,这并不是非常重要。
英文:
I have a ttl signal represented as an array of 0/1 values. I want to smooth out any short spikes where the value changes for a very short time (for instance, less than 4 samples), which I know are noise.
For instance, if 4 is the minimum legitimate pulse length:
[0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1]
should become
[0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1]
It seems a little difficult to come up with an exact replacement rule, though most cases are obvious subjectively. In general, it's not super important, as long as none of the short fluctuations are preserved.
答案1
得分: 1
你可以简单地遍历输入数组,形成另一个仅包含你想要的模式的数组。这将是一个相当简单的状态机,只是“缓冲”它经过的数组项,并消除任何短于四个项的连续出现的1,将它们替换为0。
甚至可以使用正则表达式来处理输入数组,将短时间内连续出现的1替换为0。不过,这种方法可能效率较低。
下面是演示如何实现上述状态机的PHP代码,以一种不严格优化速度或代码大小的形式编写,而是为了易于阅读和理解:
define('SPIKE_LEN', 4);
$input = [0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1];
$output = [];
$count = count($input);
$run_start = null;
for ($i = 0; $i < $count; ++$i) {
$is_one = ($input[$i] == 1);
$is_end = ($i == $count - 1);
if ($is_one && is_null($run_start))
$run_start = $i;
elseif ($is_one && !is_null($run_start) && !$is_end)
continue;
elseif ((!$is_one && !is_null($run_start))
|| ($is_one && $is_end)) {
if ($i - $run_start > SPIKE_LEN)
for ($j = $run_start; $j <= $i; ++$j)
$output[$j] = $input[$j];
else // spike
for ($j = $run_start; $j <= $i; ++$j)
$output[$j] = 0;
$run_start = null;
}
elseif (!$is_one && is_null($run_start))
$output[$i] = $input[$i];
else // shouldn't be reached
die("Internal error at position {$i} out of {$count}, value is {$input[$i]}\n");
}
echo('Input: ' . join(' ', $input) . "\n");
echo('Output: ' . join(' ', $output) . "\n");
这里还有由此代码产生的两个不同输入数组的示例输出,第一个来自原始问题:
Input: 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1
Output: 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
Input: 0 0 0 0 1 0 0 1 1 1 1 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1
Output: 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
更新: 这段代码可以轻松改进,以记录并打印输入数组中的运行大小以及每个运行大小出现的次数。这些统计数据可以提供有关输入信号性质的有用见解。
英文:
You can simply traverse the input array, forming another array that contains only the patterns you want. It would be a rather simple state machine that just "buffers" the array items it goes through and weeds out any encountered runs of ones shorter than, say, four items, replacing them with zeros.
It could even be done with a regex that treats the input array as a string and replaces short runs of ones with zeros. Though, such an approach would perhaps be less efficient.
Here's PHP code that demonstrates one way to implement the above-mentioned state machine, in a form that isn't strictly optimized for speed or code size, but written to be easily readable and understandable:
define('SPIKE_LEN', 4);
$input = [0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1];
$output = [];
$count = count($input);
$run_start = null;
for ($i = 0; $i < $count; ++$i) {
$is_one = ($input[$i] == 1);
$is_end = ($i == $count - 1);
if ($is_one && is_null($run_start))
$run_start = $i;
elseif ($is_one && !is_null($run_start) && !$is_end)
continue;
elseif ((!$is_one && !is_null($run_start))
|| ($is_one && $is_end)) {
if ($i - $run_start > SPIKE_LEN)
for ($j = $run_start; $j <= $i; ++$j)
$output[$j] = $input[$j];
else // spike
for ($j = $run_start; $j <= $i; ++$j)
$output[$j] = 0;
$run_start = null;
}
elseif (!$is_one && is_null($run_start))
$output[$i] = $input[$i];
else // shouldn't be reached
die("Internal error at position {$i} out of {$count}, value is {$input[$i]}\n");
}
echo('Input: ' . join(' ', $input) . "\n");
echo('Output: ' . join(' ', $output) . "\n");
Here are also a couple of sample outputs produced by this code, for two different input arrays, the first of which is from the OP's question:
Input: 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1
Output: 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
Input: 0 0 0 0 1 0 0 1 1 1 1 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1
Output: 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
Update: This code could easily be improved to also record and print the run sizes in the input array and how many times each run size was found. Such stats could provide a useful insight into the nature of the input signal.
答案2
得分: 0
这是一种使用简单的滑动窗口方法(窗口左侧填充第一个位和右侧填充最后一个位,并对窗口中的位进行多数投票来平滑信号的方法:
signal = [0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1]
expected = [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1]
def filter_noise(signal, window_dim):
left_pad = window_dim * [signal[0]]
right_pad = window_dim * [signal[-1]]
signal_padded = left_pad + signal + right_pad
signal_filtered = []
window_size = 2 * window_dim + 1
for i in range(len(signal)):
window = signal_padded[i:i+window_size]
bit = int(sum(window) > window_dim)
signal_filtered.append(bit)
return signal_filtered
print(filter_noise(signal, window_dim=1))
打印结果:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
在这个示例中,窗口大小为3刚好得到了期望的结果,即 filter_noise(signal, 1) == expected
。
英文:
Here's an approach that uses a simple sliding window (with left-padding of first bit and right-padding of last bit) and majority voting on the bits in the window as a way of smoothing the signal:
signal = [0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1]
expected = [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1]
def filter_noise(signal, window_dim):
left_pad = window_dim * [signal[0]]
right_pad = window_dim * [signal[-1]]
signal_padded = left_pad + signal + right_pad
signal_filtered = []
window_size = 2 * window_dim + 1
for i in range(len(signal)):
window = signal_padded[i:i+window_size]
bit = int(sum(window) > window_dim)
signal_filtered.append(bit)
return signal_filtered
print(filter_noise(signal, window_dim=1))
prints
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
In this example, a window size of 3 happens to give the desired result, i.e. filter_noise(signal, 1) == expected
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论