0

我目前正在开发一个项目来控制连接到连接到 Windows PC 的 Teensy 3.2 板的 LED 灯条。它在技术上基于这个项目: https ://www.pjrc.com/teensy/td_libs_OctoWS2811.html

在 vvvv 中还实现了一个项目: https ://vvvv.org/contribution/realtime-led-control-with-teensy3.xoctows2811

到目前为止,两者都工作正常。我想要做的是将movie2serial 程序(关于pjrc.com 上的项目)移植到Python。

所以我找到了这个项目: https ://github.com/agwn/movie2serial_py

它不是开箱即用的,但经过一些修改,我让它运行起来。这是我接收图像,将其转换为字节数组并将其发送到串行端口的类的代码:

import serial
import numpy as np

class Teensy:
  def __init__(self, port='COM3', baudrate=115200, stripes=4, leds=180):
    self.stripes = stripes
    self.leds = leds
    self.connected = True
    try:
      self.port = serial.Serial(port, baudrate)
    except:
      self.connected = False

  def close(self):
    if not self.connected:
      return
    self.black_out()
    self.port.close()

  def send(self, image):
    data = list(self.image2data(image))
    data.insert(0, 0x00)
    data.insert(0, 0x00)
    data.insert(0, ord('*'))
    if not self.connected:
      return
    self.port.write(''.join(chr(b) for b in data).encode())

  def black_out(self):
    self.send(np.zeros((self.leds,self.stripes,3), np.uint8))

  def image2data(self, image):
    buffer = np.zeros((8*self.leds*3), np.uint8)
    byte_count = 0
    order = [1,2,0]
    for led in range(self.leds):
      for channel in range(3):
        for bit in range(8):
          bits_out = 0
          for pin in range(self.stripes):
            if 0x80 >> bit & image[led,pin,order[channel]]:
              bits_out |= 1 << pin
          buffer[byte_count] = bits_out
          byte_count += 1
    return buffer

它正在工作,但速度很慢(我的电脑上大约 13 FPS)。

解释代码:我正在使用 cv2 创建一个简单的动画并将图像(具有 4 x 180 像素的 numpy ndarray,因为我有 4 个 LED 条带,每个 LED 条有 180 个 LED)发送到 Teensy 实例的发送方法。send 方法将图像发送到 image2data 方法以将图像转换为字节数组,在开头放置几个字节并将整个内容发送给 Teensy。

这段代码有两个瓶颈:

  1. 写入串行端口(方法 send 中的 self.port.write)。也许它无法加速,这是可以接受的。

但更重要的是:

  1. 访问图像数组(方法 image2data 中的 image[led,pin,order[channel]])。当我将行更改为例如:

    如果 0x80 >> 位 & 255:

代码运行速度快 6-7 倍(~ 80 FPS)。顺便说一句,order[channel] 用于将颜色从 BGR 转换为 GRB。

长话短说:从图像数组中读取颜色非常慢。如何在 image2data 方法中加快将图像数组转换为字节数组的速度?

说到这里,感谢您的耐心等待 :-) 很抱歉这篇文章很长,但这是一个复杂的项目,对我来说不容易解释。我非常感谢您的帮助,也许其他人可以从中受益。

提前致谢, 艾尔

4

2 回答 2

0

感谢您的回答和改进。我稍后会实现它们,但我猜它们不会将帧速率加快到所需的 60 FPS。

由于 Teensy 板,代码发送 3 x 180 x 8。LED 使用具有 8 个引脚的以太网电缆连接到电路板,并且所有 8 个引脚都需要寻址,否则条纹会显示奇怪的结果。另一方面,在以后的配置中,我需要超过 4 个条带,所以目前我不在乎将数据发送到 8 个条带而不是 4 个。而且我认为代码不会运行得更快。

正如我在开篇文章中提到的那样,这段代码似乎很慢,我不明白为什么:image[led,pin,order[channel]]

这是 Processing 草图中的代码,它的运行速度至少比 Python 脚本快 10 倍:

void image2data(PImage image, byte[] data, boolean layout) {
  int offset = 3;
  int x, y, xbegin, xend, xinc, mask;
  int linesPerPin = image.height / 8;
  int pixel[] = new int[8];
  for (y = 0; y < linesPerPin; y++) {
    if ((y & 1) == (layout ? 0 : 1)) {
      xbegin = 0;
      xend = image.width;
      xinc = 1;
    } else {
      xbegin = image.width - 1;
      xend = -1;
      xinc = -1;
    }
    for (x = xbegin; x != xend; x += xinc) {
      for (int i=0; i < 8; i++) {
        pixel[i] = image.pixels[x + (y + linesPerPin * i) * image.width];
        pixel[i] = colorWiring(pixel[i]);
      }
      for (mask = 0x800000; mask != 0; mask >>= 1) {
        byte b = 0;
        for (int i=0; i < 8; i++) {
          if ((pixel[i] & mask) != 0) b |= (1 << i);
        }
        data[offset++] = b;
      }
    }
  } 
}

我不敢相信 Python 比 Java 慢得多。我仍然希望有人知道访问 numpy 数组的像素有什么问题。

于 2019-02-25T16:36:26.380 回答
0

第二个热点可以通过order[channel]在该内部循环之外提升(通过在循环内部保存channel_index = order[channel])来稍微改进order,然后编写

if 0x80 >> bit & image[led,pin,channel_index]:

这将是一个小的改进。看起来提升0x80 >> bit水平也可以节省8次冗余计算。将其另存为mask,您将拥有

if mask & image[led,pin,channel_index]:

总之,这些可能值几个 FPS。

但是看看你的代码,这些循环的嵌套方式看起来不对劲。对于 180 x 4 RGB LED,我希望您需要向 Teensy 发送 180 x 4 x 3 字节。但是代码发送的是 3 x 180 x 8。是否有可能需要反转两个内部循环?

于 2019-02-23T03:25:20.997 回答