英文:
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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论