关于python:提高条件语句的CPU时间

Improve CPU time of conditional statement

我写了一份if-elif声明,我认为这不是很有效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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时间吗?

我试过这个答案,但我的陈述条件不同。

谢谢您。


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

Edge image

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

1
2
3
4
5
6
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-lefttop-midtop-right

mid-leftmid-midmid-right

bot-leftbot-midbot-right

现在,为了简单一点,我们可以按照块所在的顺序列出列表字典中的键;这样,blocks[0]将对应于switch_list[0]"upperLeft"

1
2
3
4
5
switch_list = [
    'upperLeft', 'upperMiddle', 'upperRight',
    'middleLeft', 'middle', 'middleRight',
    'lowerLeft', 'lowerMiddle', 'lowerRight'
    ]

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

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

只有当所有三个值都匹配时,像素才是黑色的,所以我们可以在结果上使用.all(),如果整个数组是True,则返回True

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

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

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

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

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

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

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

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

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

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

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

最后,print的结果是:

1
2
3
4
5
6
7
8
9
10
>>> print(switch)
{'upperRight': True,
'upperLeft': True,
'lowerRight': True,
'lowerLeft': True,
'middleLeft': True,
'middleRight': True,
'upperMiddle': True,
'lowerMiddle': True,
'middle': False}

仅在这里进行的操作在(21402870)图像上花费了约0.1秒。

在同一行中,您可以首先创建一个由TrueFalse值组成的矩阵,将整个图像与.all()一起分割成块,然后在块内使用.any()。这对内存更好,因为您存储的是9个(h,w)块,而不是9个(h,w,depth)块。


在这种情况下,您可以避免整个if/elif/else混乱,因为您的条件可以由直接从ij值计算到字典中的相应键来代替。

1
2
3
4
5
6
7
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_number可能变成widthsecond_number可能变成height(反之亦然),ij值可能变成xy(尽管后两个比较差,因为ij是相当传统的循环变量)。

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