在Python中通过循环修改字节串的最快方法吗?

问题描述 投票:0回答:1

我将图像存储为字节串b'',并且正在执行每个像素的操作。现在,我发现最快的方法是在修改过程中使用struct条板箱打包和解压缩字节,然后将像素保存到bytearray

# retrieve image data. Stored as bytestring
pixels = buff.get(rect, 1.0, "CIE LCH(ab) alpha double",
                  Gegl.AbyssPolicy.CLAMP)
# iterator split into 32-byte chunks for each pixel's 8-byte LCHA channels
pixels_iter = (pixels[x:x + 32] for x in range(0, len(pixels), 32))
new_pixels = bytearray()

# when using `pool.map`, the loop was placed in its own function.
for pixel in pixels_iter:
    l, c, h, a = struct.unpack('dddd', pixel)
    # simple operation for now: lower chroma if bright and saturated
    c = c - (l * c) / 100
    new_pixels += struct.pack('dddd', l, c, h, a)

# save new data. everything hereout handled by GEGL instead of myself.
shadow.set(rect, "CIE LCH(ab) alpha double", bytes(new_pixels))

问题是,在我的工作站上获取7MP映像大约需要3 1/2秒。一般,但如果经常要求更新,则不理想。从我收集的数据来看,常量数组修改和struct [un] packing似乎是主要的罪魁祸首。我已经将它重构了十几次,并且我认为我在优化这一点的Python机智中处于尽头。

我尝试过:

  • struct.unpack整个字节串一次,而不是每个像素一次。损失约20%的效率。
  • collections.deque当然不熟悉其技术。损失10-30%,具体取决于实施情况
    • 与其他迭代器帮助器,例如map / join,具有相似的结果
  • [numpy.array]当然,对于一般的numpy基本上一无所知。与双端队列相似的结果
  • multiprocessing似乎是瓶颈,当我将pool.map结果附加到new_pixels时。实际上损失了大约10%,这似乎很疯狂,因为通常我只能懒洋洋地将线程扔到问题上。每个线程将pixels_iter分组为大小相等的子列表,因此new_pixels将8个大列表而不是几百万个小列表连接在一起,我认为这样会更快。由于我可能会在凌晨4点实施时以某种方式破坏了它,因此尝试重试它。
      理论上,它也可以通过保存图像缓冲区的多个小部分来避免完全串联为new_pixels来工作,但这将极大地增加其他地方的代码复杂性。
  • pixels本身转换为bytearray,并使用切片范围对其进行就地修改。丢失了约30%,但内存使用量也减半。
  • 像Pypy这样的完全独立的解释器已经摆在桌面上,因为我不是捆绑Python版本的人。

    我有一个图像存储为字节串b'',并且正在执行每个像素的操作。现在,我发现最快的方法是在修改过程中使用struct crate打包和解压缩字节,...

  • python arrays loops byte
    1个回答
    0
    投票
    NumPy如果使用得当,它的生成结果比手动循环要快得多。正确使用它意味着在整个数组上使用NumPy操作,而不仅仅是在NumPy数组上手动循环。
    © www.soinside.com 2019 - 2024. All rights reserved.