在opencv python中加快每个像素循环的速度

时间:2019-06-21 14:19:26

标签: python arrays opencv

因此,我尝试查找非白色的像素,并通过检查颜色在图像周围创建边框。我想获取最顶部,最底部,最左侧和最右侧的非白色像素,并使用它们来创建边界框。我已经使用了四个循环遍历每侧。我还想做的是去除背景色(背景色大多为灰色)并将其更改为纯白色。我已经实现了所有功能,但是现在由于使用了大量循环,因此代码运行速度太慢。我需要优化循环,同时还要具有找到最上面,最下面,最左边和最右边的非白色像素并删除颜色的功能。我该怎么办?

下面的代码显示了我正在做的事情,以便同时获得边框和背景移除。遮罩是图像的黑白版本。如果它是mask [i] [j] == 0,那么它是另一种颜色,因此我需要获取该值并将其与存储在p处的值进行比较。它可以帮助我找到边界框。如果mask [i] [j]!= 0,那么我会将图像的值更改为白色。

//for bounding box
p = []
p.append(5000)
p.append(0)
p.append(5000)
p.append(0)


for i in range(0, height):
    for j in range(0, width):
        if mask[i][j] == 0:
            if j < p[0]:
                p[0] = j
            break
        else:
            img[i, j] = [255, 255, 255]

for i in range(0, height):
    for j in reversed(range(0, width)):
        if mask[i][j] == 0:
            if j > p[1]:
                p[1] = j
            break
        else:
            img[i, j] = [255, 255, 255]

//topdown
for i in range(0, width):
    for j in range(0, height):
        if mask[j][i] == 0:
            if j < p[2]:
                p[2] = j
            break
        else:
            img[j, i] = [255, 255, 255]

for i in reversed(range(0, width)):
    for j in reversed(range(0, height)):
        if mask[j][i] == 0:
            if j > p[3]:
                p[3] = j
            break
        else:
            img[j, i] = [255, 255, 255]

那么我如何在优化这些循环的同时仍获得获取像素值并能够更改其他图像颜色的相同功能?

2 个答案:

答案 0 :(得分:1)

背景
要使背景变白,您可以对遮罩使用按位操作。要自动创建遮罩,请阅读here

示例:
enter image description here

import cv2
import numpy as np

# load image and mask
img = cv2.imread('image.png')
mask = cv2.imread('mask.png')

# combine images
res = cv2.bitwise_or(img,mask)

cv2.imshow("result", res)
cv2.waitKey(0)
cv2.destroyAllWindows() 

遮罩必须具有与图像相同数量的颜色通道。蒙版中的所有白色区域也将在图像中变为白色。蒙版中的黑色区域在图像中将保持不变。

边界框
要获取边界框,您可以使用findContours。它以二进制掩码作为输入并返回轮廓列表。您可以使用轮廓找到boundingbox, rotated boundingbox or minimum enclosing circle。结果可能并不完美,具体取决于您的输入,但是您可以使用它来提高性能,因为它极大地缩小了搜索范围。

注意:findContours的输入应具有黑色背景。您可以使用inverted_mask = cv2.bitwise_not(mask)修改遮罩。或者,如果您使用阈值遮罩获得遮罩,则可以选择倒置的threshold type

结果:
enter image description here

代码:

    import cv2
    import numpy as np

    # load image // use your mask instead
    mask = cv2.imread('mask.png',0)

    # find contours 
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

for cnt in contours:
        # get the boundingrect and draw a red line over it
        x,y,w,h = cv2.boundingRect(cnt)
        cv2.rectangle(mask2,(x,y),(x+w,y+h),(0,0,255),3)
        # get the minumum enclosing rectangle and draw it in blue
        rect = cv2.minAreaRect(cnt)
        box = cv2.boxPoints(rect)
        box = np.int0(box)
        cv2.drawContours(mask2,[box],0,(255,0,0),3)

    # diplay result
    cv2.imshow("img", mask)
    cv2.waitKey(0)
    cv2.destroyAllWindows() 

数学
如果您想坚持检查数组值,则可以通过先对行和列求和来提高性能。求和速度很快(并变为numpy),现在您可以通过检查一个值来丢弃行/列。您可以在this answer中看到此过程的示例。我建议为此使用背景黑色的蒙版,因为您可以将总和与零进行比较。这实际上将导致上面的红色边框。当然,当找到一个非零的行/列时,您仍然必须循环该行以找到确切的坐标。

Pfiew,结果比预期的要长得多...

答案 1 :(得分:0)

@JohnColeman有一点。即使使用最佳算法,嵌套的Python循环也会相对较慢,并且有一些库可以优化此类操作。

通过使用每个循环的结果来限制后续循环的范围,可以加快算法本身的速度。例如,如果在寻找顶部非白色像素时,您是从上到下扫描为外循环,从左到右为内循环,则找到了一个像素(a, b)(其中a是与顶部),然后在下一部分中寻找左像素,您知道可以从上至下的外循环中的a + 1开始扫描,而从左至右的内循环中的b-1开始扫描。我们称结果为(c,d)。

类似地,底部像素在垂直方向上不得小于c,在水平方向上不得小于d。