简体   繁体   English

OpenCV - 拆分和合并阿尔法通道很慢

[英]OpenCV - Splitting and merging alpha channels slow

I am using Python OpenCV to split channels and remove black background like this...我正在使用 Python OpenCV 来分割通道并像这样删除黑色背景......

    b_channel, g_channel, r_channel = cv2.split(image_1)
    alpha_channel = np.zeros_like(gray)

    for p in range(alpha_channel.shape[0]):
        for q in range(alpha_channel.shape[1]):
            if b_channel[p][q]!=0 or g_channel[p][q]!=0 or r_channel[p][q]!=0:
                alpha_channel[p][q] = 255

    merged = cv2.merge((b_channel, g_channel, r_channel, alpha_channel))

This is working, but it is taking around 10 seconds to complete on an image that is only 200kb这是可行的,但在仅 200kb 的图像上完成大约需要 10 秒

Is there a more efficient way to do this or is there some speed gains I could make using the code I have?有没有更有效的方法来做到这一点,或者我可以使用我拥有的代码获得一些速度提升?

Iterating over pixels using for loop is literally very slow and inefficient. 使用for循环遍历像素实际上非常缓慢且效率低下。 Also, as per the documentation here , 另外,根据此处的文档,

cv2.split() is a costly operation (in terms of time). cv2.split()是一项昂贵的操作(就时间而言)。 So do it only if you need it. 因此,仅在需要时才这样做。 Otherwise go for Numpy indexing. 否则请进行Numpy索引。

You can try vectorising and indexing with numpy as below: 您可以尝试使用numpy进行矢量化和索引编制,如下所示:

# create the image with alpha channel
img_rgba = cv2.cvtColor(img, cv2.COLOR_RGB2RGBA)

# mask: elements are True any of the pixel value is 0         
mask = (img[:, :, 0:3] != [0,0,0]).any(2) 
#assign the mask to the last channel of the image
img_rgba[:,:,3]  = (mask*255).astype(np.uint8)

For what you're doing, using cv2.bitwise_or seems to be the fastest method: 对于您正在做的事情,使用cv2.bitwise_or似乎是最快的方法:

image_1 = img
# your method
start_time = time.time()
b_channel, g_channel, r_channel = cv2.split(image_1)
alpha_channel = np.zeros_like(gray)
for p in range(alpha_channel.shape[0]):
    for q in range(alpha_channel.shape[1]):
        if b_channel[p][q]!=0 or g_channel[p][q]!=0 or r_channel[p][q]!=0:
            alpha_channel[p][q] = 255
elapsed_time = time.time() - start_time
print('for cycles:  ' + str(elapsed_time*1000.0) + ' milliseconds')

# my method
start_time = time.time()
b_channel, g_channel, r_channel = cv2.split(image_1)
alpha_channel2 = cv2.bitwise_or(g_channel,r_channel)
alpha_channel2 =  cv2.bitwise_or(alpha_channel2, b_channel)
_,alpha_channel2 = cv2.threshold(alpha_channel2,0,255,cv2.THRESH_BINARY)
elapsed_time2 = time.time() - start_time
print('bitwise + threshold:  '+ str(elapsed_time2*1000.0) + ' milliseconds')

# annubhav's method
start_time = time.time()
img_rgba = cv2.cvtColor(image_1, cv2.COLOR_RGB2RGBA)
# mask: elements are True any of the pixel value is 0         
mask = (img[:, :, 0:3] != [0,0,0]).any(2) 
#assign the mask to the last channel of the image
img_rgba[:,:,3]  = (mask*255).astype(np.uint8)
elapsed_time3 = time.time() - start_time
print('anubhav:  ' + str(elapsed_time3*1000.0) + ' milliseconds')

for cycles: 2146.300792694092 milliseconds 周期:2146.300792694092毫秒

bitwise + threshold: 4.959583282470703 milliseconds 按位+阈值:4.959583282470703毫秒

anubhav: 27.924776077270508 milliseconds anubhav:27.924776077270508毫秒

Fastest Solution最快的解决方案

Let us consider a function that uses cv2.split and we know that it is very inefficient, we can go ahead and resize or crop a certain part of the image and then perform our calculation on that.让我们考虑一个使用cv2.split的函数,我们知道它的效率非常低,我们可以继续调整或裁剪图像的某个部分,然后对其进行计算。 In my case where I had to calculate the colorfulness of the image using cv2.split I went ahead and resized and cropped the image to make cv2.split work.在我必须使用cv2.split计算图像的色彩度的情况下,我继续调整大小并裁剪图像以使cv2.split工作。

  • A faster and more reasonable cv2.split calculation can be performed by Resizing可以通过Resizing进行更快更合理cv2.split计算

Code代码

def image_colorfulness(self,image):
        # split the image into its respective RGB components
        (B, G, R) = cv2.split(image.astype("float"))
        print(f'Split Image to B G R {(B, G, R)}')
        # compute rg = R - G
        rg = np.absolute(R - G)
        print(f'Computed RG to {rg}')
        # compute yb = 0.5 * (R + G) - B
        yb = np.absolute(0.5 * (R + G) - B)
        # compute the mean and standard deviation of both `rg` and `yb`
        print('Performing Absolute')
        (rbMean, rbStd) = (np.mean(rg), np.std(rg))
        (ybMean, ybStd) = (np.mean(yb), np.std(yb))
        # combine the mean and standard deviations
        print('Performing Standard Deviation')
        stdRoot = np.sqrt((rbStd ** 2) + (ybStd ** 2))
        meanRoot = np.sqrt((rbMean ** 2) + (ybMean ** 2))
        # derive the "colorfulness" metric and return it
        return stdRoot + (0.3 * meanRoot)


def crop_square(self, img, size, interpolation=cv2.INTER_AREA):
        h, w = img.shape[:2]
        min_size = np.amin([h,w])

        # Centralize and crop
        crop_img = img[int(h/2-min_size/2):int(h/2+min_size/2), int(w/2-min_size/2):int(w/2+min_size/2)]
        resized = cv2.resize(crop_img, (size, size), interpolation=interpolation)

        return resized


img = cv2.imread(image_path)
resize_img = self.crop_square(img, 300)
## perform your calculation on the resized_img and continue with the original img then 
colorness =  self.image_colorfulness(resize_img)

Resizing Only仅调整大小

If you prefer not to crop and only resize the image, that can be achieved by taking a look at this line of code from the square_crop function.如果您不想裁剪而只调整图像大小,可以通过查看square_crop函数中的这行代码来实现。

resized = cv2.resize(crop_img, (size, size), interpolation=interpolation)

Testing Results测试结果

Before

  • I tested a 5.0 MB *.PNG Image, before using standard image input in cv2.split it processed in 8 Minutes .我测试了一个5.0 MB *.PNG图像,然后在cv2.split中使用标准图像输入,它在8 Minutes中处理。

After

After the Image Resizing it was reduced to 0.001 ms on the resized image.在图像调整大小之后,调整大小的图像上的时间减少到0.001 ms

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM