【问题标题】:Python if-elif-else runtime optimizationPython if-elif-else 运行时优化
【发布时间】:2018-09-24 15:24:54
【问题描述】:

我对上一个问题进行了搜索,但没有找到优化代码所需的内容。

关于信息,我在 Python 2.7 上运行,但如果需要可以更改为 3

我正在转换图像的每个像素,由于某些条件,我必须逐个像素地进行转换。 所以我在里面嵌套了一个 if-elif-else 语句的 for 循环,它需要很长时间才能运行。 对于 1536 x 2640 的图像,整个代码大约需要 20 秒,90% 的时间都在这个双 for 循环中

我相信应该有更好的方法来编写下面的代码

  for pixel in range(width):
    for row in range(height):
      ADC = img_original[row, pixel]
      if ADC < 84:
        gain   = gain1
        offset = offset1
      elif ADC > 153: 
        gain   = gain3
        offset = offset3
      else:
        gain   = gain2
        offset = offset2

      Conv_ADC  = int(min(max(ADC * gain + offset, 0),255))
      img_conv[row, pixel] = Conv_ADC

感谢您的帮助


编辑以获取更多详细信息:

@Jean-FrançoisFabre 是对的,我根据我在 0 到 255 之间的哪个部分应用三种不同的增益/偏移量。但该部分并不总是均匀的空间,可以修改。 也许为了提供一些额外的上下文,我只是将自定义 S 曲线应用于图像以向上/向下移动像素值。并且图像中的每一列都有自己的S曲线

我的 gain1,2,3/offset1,2,3 值是浮点数。增益将始终为正,偏移量可以为负或正。我在宽度方向上也为每个像素设置了一个单独的值,但它们在行方向上是通用的。

例如,第 1 列中的所有像素都可以使用下表中第一行的增益/偏移 1、2、3。图像中第 2 列的所有像素将使用下表第 2 行的增益/偏移量

Pixel   Gain1     Offset1    Gain2     Offset2   Gain3     Offset3
1       0.417722  24.911392  0.623188  7.652176  1.175676  -76.878357
2       0.43038   25.848103  0.623188  9.652176  1.148649  -70.743225
3       0.443038  23.784809  0.637681  7.434776  1.175676  -74.878357
4       0.443038  22.784809  0.652174  5.217384  1.175676  -74.878357
5       0.455696  23.721519  0.637681  8.434776  1.202703  -78.013519
6       0.455696  21.721519  0.637681  6.434776  1.243243  -86.216217
7       0.455696  22.721519  0.623188  8.652176  1.216216  -82.081085
8       0.443038  22.784809  0.623188  7.652176  1.22973   -85.148651
... until pixel 2640 in width direction

我会看看@Jean-FrançoisFabre 解决方案,但同时我也在考虑使用一些 numpy 方法。

一旦我得到计算速度更快的东西,我会在这里发布我的发现

【问题讨论】:

  • 我想你会想看看 numpy.where() 的矢量化方法,但我们看不到数据
  • 问题可能不是if-elif-statement,而是for循环。如果您在循环中花费大量时间,那是因为您的代码很慢。我支持@roganjosh 使用矢量化方法。
  • 您可以使用多处理将工作负载拆分为像素网格的较小部分。这样,如果需要像您一样循环遍历像素,它可以使完成速度更快。
  • 现在在电脑上。你能提供一个我可以测试的有代表性的输入和输出吗?

标签: python python-2.7 if-statement optimization


【解决方案1】:

由于您的值介于 0 和 255 之间,并且您的边界是均匀分布的,您可以使用以下技巧:

您似乎想要应用 3 种不同的增益,具体取决于您是在 0-255 范围的前三分之一、第二个三分之一还是第三个三分之一。

为什么不通过除以 85 (255/3) 来计算索引?

简单的概念证明:

gainsoffsets = [(10,1),(20,2),(30,3),(30,3)] # [(gain1,offset1),(gain2,offset2),(gain3,offset3)] + extra corner case for value 255

for value in 84,140,250:
    index = value // 85
    gain,offset = gainsoffsets[index]
    print(gain,offset)

结果:

10 1
20 2
30 3

在这个循环中只有一个除法,没有if。应该更快(除了numpy 方法)

您可以使用更精细的级别和更准确的查找表(也可以通过生成 256 个元组来避免除法):

gainsoffsets = [(10,1)]*85+[(20,2)]*85+[(30,3)*86]  # add more intervals for more thresholds

【讨论】:

  • max(ADC * gain + offset, 0) 是什么让我失望。为什么他们期望负面结果?
  • 也许偏移量是负数。你不知道。
  • 是的,大概就是这样。在我尝试 numpy 方法之前,我已经要求他们提供一个示例供我测试
【解决方案2】:

尝试使用查找表:您预先计算 [0,255] 范围内的所有转换后的 ADC 值,循环体将简化

  for pixel in range(width):
    for row in range(height):
      img_conv[row, pixel]= LUT[img_original[row, pixel]]

【讨论】:

  • 这可能是一个很好的解决方案。尽管每一列都有自己的 LUT,因为它们都使用不同的偏移/增益值。它只是一个更大的 LUT,但原理相同。
  • @Sebdarmy:帖子中没有提到这一点。那么 LUT 的吸引力就会降低,因为 LUT 的初始化成本很高。
【解决方案3】:

这是我用来删除 2 个 for 循环的最终实现。每张图像减少到大约 1~2 秒

我正在创建 3 个数组,当内容不在我想要的范围内时,我会将其替换为 0。然后在添加整体之前对每个进行增益乘法和偏移

  height = img_original.shape[0]
  width  = img_original.shape[1]
  print 'height = ', height, 'width = ', width

  # create temp 3 2D-arrays
  img1 = np.array(img_original,dtype=np.int)
  img2 = np.array(img_original,dtype=np.int)
  img3 = np.array(img_original,dtype=np.int)

  #create the 2D array for gain/offset based on 1D array
  # csv array acquire from .csv file, INDEX_xx for column to read
  array_gain1    = np.tile(csv[1:, INDEX_G1],(height,1))
  array_offset1  = np.tile(csv[1:, INDEX_O1],(height,1))

  array_gain2   = np.tile(csv[1:, INDEX_G2],(height,1))
  array_offset2 = np.tile(csv[1:, INDEX_O2],(height,1))

  array_gain3    = np.tile(csv[1:, INDEX_G3],(height,1))
  array_offset3  = np.tile(csv[1:, INDEX_O3],(height,1))

  # replace the content by 0 when not in the desired zone
  np.place(img1,img_original >= G2_TARGET, 0) 
  np.place(img2,img_original < G2_TARGET,0)   
  np.place(img2,img_original > G1_TARGET,0)   
  np.place(img3,img_original <= G1_TARGET, 0) 

  np.place(array_offset1,img_original >= G2_TARGET, 0)
  np.place(array_offset2,(img_original < G2_TARGET), 0)
  np.place(array_offset2,(img_original > G1_TARGET), 0)
  np.place(array_offset3,img_original <= G1_TARGET, 0)

  # apply the gain/offset for each zone
  img1 = np.array(img1 * array_gain1 + array_offset1, dtype=np.uint8)
  img2 = np.array(img2 * array_gain2 + array_offset2, dtype=np.uint8)
  img3 = np.array(img3 * array_gain3 + array_offset3, dtype=np.uint8)

  # recrete the whole image
  img_conv = np.clip(img1 + img2 + img3, 0, 255)

【讨论】:

    最近更新 更多