改善条件语句的CPU时间

时间:2017-06-28 09:17:46

标签: python performance if-statement

我写了一个if-elif语句,我认为这不是很有效:

first_number = 1000
second_number = 700
switch = {
    'upperRight': False,
    'upperLeft': False,
    'lowerRight': False,
    'lowerLeft': False,
    'middleLeft': False,
    'middleRight': False,
    'upperMiddle': False,
    'lowerMiddle': False,
    'middle': False
}

for i in range(first_number):
    for j in range(second_number):
        if pixel_is_black:
            if i <= int(first_number/3) and j <= int(second_number/3):
                switch['upperLeft'] = True
            elif i <= int(first_number/3) and int(second_number/3) < j <= int(2*second_number/3):
                switch['middleLeft'] = True
            elif i <= int(first_number/3) and j > int(2*second_number/3):
                switch['lowerLeft'] = True
            elif int(first_number / 3) <= i < int(2 * first_number / 3) and j < int(second_number / 3):
                switch['upperMiddle'] = True
            elif int(first_number / 3) <= i < int(2 * first_number / 3) and int(second_number / 3) < j <= int(2 * second_number / 3):
                switch['middle'] = True
            elif int(first_number / 3) <= i < int(2 * first_number / 3) and j >= int(2 * second_number / 3):
                switch['lowerMiddle'] = True
            elif i >= int(2 * first_number / 3) and j <= int(2 * second_number / 3):
                switch['upperRight'] = True
            elif i >= int(2 * first_number / 3) and int(second_number / 3) < j <= int(2 * second_number / 3):
                switch['middleRight'] = True
            elif i >= int(2 * first_number / 3) and  j >= int(2 * second_number / 3):
                switch['lowerRight'] = True

for i in switch:
    if(switch[i] == True):
        print(i)

如您所见,该陈述看起来非常难看。 由于数字很大,执行时间大约需要2秒。  在循环中,我将浏览图像的像素。在if-elif语句中,我将图像分成9个部分,如果该区域中的像素为黑色,则打印相应的数字。

有什么方法可以降低CPU时间吗?

我试过this answer,但我的陈述条件不同。

谢谢。

2 个答案:

答案 0 :(得分:2)

由于您正在使用图像和使用numpy,我认为最简单的方法是将图像分割成块并查看这些块内的任何像素是否为黑色。例如,假设我有一个边缘图像,其中中间没有任何黑色像素,如下所示:

Edge image

我们可以使用列表推导将图像转换为块:

h, w = img.shape[:2]
bh, bw = h/3, w/3

bw_ind = [0, int(bw), 2*int(bw), w]
bh_ind = [0, int(bh), 2*int(bh), h]
blocks = [img[bh_ind[i]:bh_ind[i+1], bw_ind[j]:bw_ind[j+1]] for i in range(3) for j in range(3)]

top-left top-mid top-right

mid-left mid-mid mid-right

bot-left bot-mid bot-right

现在只是为了让事情变得更简单,我们可以按照块所在的顺序列出列表字典中的键列表;这样,blocks[0]将对应switch_list[0] "upperLeft"

switch_list = [
    'upperLeft', 'upperMiddle', 'upperRight',
    'middleLeft', 'middle', 'middleRight',
    'lowerLeft', 'lowerMiddle', 'lowerRight'
    ]

然后,最后要做的是找到每个块中的黑色像素。因此,我们想要遍历9个块(使用循环),然后将块内的值与我们感兴趣的颜色进行比较。如果您有3通道8位图像,则通常表示黑色每个频道都有0。因此对于单个像素,如果它是黑色的,那么我们可以将它与具有pixel == [0,0,0]的黑色进行比较。但是这会为每个值返回一个布尔值:

>>> pixel == [0,0,0]
[True, True, True]

当所有这三个值匹配时,像素仅为黑色,因此我们可以对结果使用.all(),如果整个数组为True,则仅返回True:< / p>

>>> (pixel == [0,0,0]).all()
True

所以这是我们的指标,单个像素是黑色的。但我们需要检查任何像素是否在我们的块中是黑色的。为了简单起见,我们先来看一个单通道图像。假设我们有数组

M = np.array([[0,1], [2,3]])

如果我们在这里使用逻辑比较M == 5,我们会返回一个布尔数组,形状与M相同,将每个元素与5进行比较:

>>> M == 5
array([[False, False] [False, False]])

在我们的例子中,我们不需要知道每个比较,我们只想知道块内的单个像素是否为黑色,所以我们只需要一个布尔值。我们可以使用.any()来检查True {/ 1>}的 {/ 1>}是否为M

>>> (M == 5).any()
False

所以我们需要结合这两个操作;我们确保所有值与我们感兴趣的颜色([0,0,0])匹配,以计算该像素,然后我们可以看到是否 每个块内的比较返回True我们的像素:

black_pixel_in_block = [(block==[0,0,0]).all(axis=2).any() for block in blocks] 

注意axis=2参数:.all(axis=2)会将多通道图像缩减为单个布尔通道;如果颜色在每个频道中匹配,则在像素位置True。然后我们可以检查任何像素位置是否返回true。这减少到每个块的布尔值,告诉它是否包含颜色。因此,我们可以根据是否找到黑色像素将字典值设置为TrueFalse

for i in range(len(switch_list)):
    switch[switch_list[i]] = black_pixel_in_block[i]

最后,print结果:

>>> print(switch)
{'upperRight': True, 
'upperLeft': True, 
'lowerRight': True, 
'lowerLeft': True, 
'middleLeft': True, 
'middleRight': True, 
'upperMiddle': True, 
'lowerMiddle': True, 
'middle': False}

这里的操作在(2140,2870)图像上花费了大约0.1秒。

沿着相同的行,您可以首先使用True为整个图像创建False.all()值的矩阵,然后将 分成阻止,然后在块内使用.any()。这样可以更好地记忆,因为您需要存储9个(h,w)块而不是9个(h,w,depth)块。

答案 1 :(得分:1)

在这种情况下,您可以避免整个if / elif / else混乱,因为您的条件可以通过直接计算来自{的字典中的相应键来替换{1}}和i值。

j

请注意,为某些变量提供更有意义的名称可能是个好主意。 for i in range(first_number): horizontal_third = i * 3 // first_number for j in range(second_number): if pixel_is_black(i, j): # I assume this depends on the coordinates in some way vertical third = j * 3 // second_number key = str(horizontal_third * 3 + vertical_third + 1) # use int keys? zero-index? switch[key] = True 可能会成为first_numberwidth可能会成为second_number(反之亦然),heighti值可能会变为j }和x(虽然后两者不太糟糕,因为yi非常传统的循环变量。)

如果您愿意稍微更改j以进一步提高性能,可以使用从零开始的整数作为索引替换字典。您只需要从switch计算中删除+ 1str来电(我还要重命名变量key)。您可以使用index初始化列表。