【问题标题】:Can you translate a flashing light into morse code?你能把闪光灯翻译成莫尔斯电码吗?
【发布时间】:2021-10-05 21:38:19
【问题描述】:

我制作了一个莫尔斯电码翻译器,我希望它能够记录闪烁的灯光并将其转换为莫尔斯电码。我想我需要 OpenCV 或光传感器,但我不知道如何使用它们中的任何一个。我还没有任何代码,因为我在其他任何地方都找不到任何解决方案。

【问题讨论】:

  • 请阅读How to ask
  • 使用 OpenCV 的相机输入可能是最简单的。您真正需要做的就是阅读每一帧,确定它们是否点亮,并记录点亮的帧变为未点亮的时间,反之亦然,然后将其转换为莫尔斯序列。 (您可能希望查看每个点亮周期的平均长度,并将比它短的视为点,将较长的视为破折号。)
  • 但答案是:是的,这绝对是可能的,但这取决于您的要求。最简单的事情是,假设图像有手电筒,例如在图像的中心,简单地计算每个图像中的平均亮度或某个区域,并跟踪每个正边缘(灯亮)直到下一个负边缘(灯熄灭)的持续时间。现在,您只需按长度将信号分为两类,您(基本上)就完成了。
  • 你的闪光灯是什么格式的?比如你的电脑是怎么看的?还是您没有任何计划?........如果您没有任何计划,请先找到树莓派和光传感器。
  • @StefanFalk 这听起来像是可行的,我一直在寻找如何做到这一点,但没有找到教程。你怎么能做到?

标签: python morse-code


【解决方案1】:

以下只是您可以尝试的概念。是的,你也可以为此训练一个神经网络,但如果你的设置足够简单,一些工程就可以了。

我们首先创建一个“玩具视频”来使用:

import numpy as np
import matplotlib.pyplot as plt

# Create a toy "video"

image = np.asarray([
    [0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0],
    [0, 0, 1, 2, 2, 1],
    [0, 0, 2, 4, 4, 2],
    [0, 0, 2, 4, 4, 2],
    [0, 0, 1, 2, 2, 1],
])

signal = np.asarray([0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0])
x = list(range(len(signal)))
signal = np.interp(np.linspace(0, len(signal), 100), x, signal)[..., None]
frames = np.einsum('tk,xy->txyk', signal, image)[..., 0]

绘制几帧:

fig, axes = plt.subplots(1, 12, sharex='all', sharey='all')
for i, ax in enumerate(axes):
    ax.matshow(frames[i], vmin=0, vmax=1)
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    ax.set_title(i)
plt.show()

现在您有了这种玩具视频,将其转换回某种二进制信号非常简单。您只需计算每帧的平均亮度:

reconstructed = frames.mean(1).mean(1)
reconstructed_bin = reconstructed > 0.5
plt.plot(reconstructed, label='original')
plt.plot(reconstructed_bin, label='binary')
plt.title('Reconstructed Signal')
plt.legend()
plt.show()

从这里我们只需要确定每次闪光的长度。

# This is ugly, I know. Just for understanding though:
# 1. Splits the binary signal on zero-values
# 2. Filters out the garbage (accept only lists where len(e) > 1)
# 3. Gets the length of the remaining list == the duration of each flash
tmp = np.split(reconstructed_bin, np.where(reconstructed_bin == 0)[0][1:])
flashes = list(map(len, filter(lambda e: len(e) > 1, tmp)))

我们现在可以看看闪烁需要多长时间:

print(flashes)

给我们

[5, 5, 5, 10, 9, 9, 5, 5, 5]

所以..“短”闪光似乎需要 5 帧,“长”大约需要 10 帧。这样,我们可以通过定义一个合理的阈值 7 来将每个闪光分类为“长”或“短”,如下所示:

# Classify each flash-duration
flashes_classified = list(map(lambda f: 'long' if f > 7 else 'short', flashes))

让我们重复暂停

# Repeat for pauses
tmp = np.split(reconstructed_bin, np.where(reconstructed_bin != False)[0][1:])
pauses = list(map(len, filter(lambda e: len(e) > 1, tmp)))
pauses_classified = np.asarray(list(map(lambda f: 'w' if f > 6 else 'c', pauses)))
pauses_indices, = np.where(np.asarray(pauses_classified) == 'w') 

现在我们可以将结果可视化。

fig = plt.figure()
ax = fig.gca()
ax.bar(range(len(flashes)), flashes, label='Flash duration')
ax.set_xticks(list(range(len(flashes_classified))))
ax.set_xticklabels(flashes_classified)
[ax.axvline(idx-0.5, ls='--', c='r', label='Pause' if i == 0 else None) for i, idx in enumerate(pauses_indices)]
plt.legend()
plt.show()

【讨论】:

  • 对一个关于 stackoverflow 的随机问题的有趣回答 - 值得点赞!
  • :D 谢谢。多考虑一下,这可能会很多更干净,但是..它只是应该解释主要思想。我猜剩下的就是 OP 的工作了 :)
  • 几个月来我读过的最有趣、设计精美的答案! ?
  • 我在 Trinket 上试过这个,但没有用。它返回AttributeError: '<invalid type>' object has no attribute 'interp' on line 17 in main.py。我该如何解决这个问题,或者这不能在 Trinket 上运行
  • @Pazzel 那么你可能错误地导入了 numpy 模块。
【解决方案2】:

这在一定程度上取决于您的环境。您可以以 1 英镑而不是 100 英镑的价格尝试使用 Raspberry Pi Zero(9 英镑)甚至 Pico(4 英镑)或 Arduino 以及附加的 LDR - 光敏电阻,价格低廉USB 摄像头。

然后您的程序将归结为重复测量电阻(取决于光强度)并将其制成长脉冲和短脉冲。

这样做的好处是便宜且不需要您学习 OpenCV,但 Stefan 的想法更有趣,并且得到了我的投票!

【讨论】:

  • 这听起来也不错。你会怎么做?我有所有的东西,但我不知道如何把它们放在一起。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-10
  • 1970-01-01
  • 2020-09-19
  • 2014-09-18
相关资源
最近更新 更多