繁体   English   中英

Python 向量化图像处理中的嵌套 for 循环

[英]Python vectorizing nested for loops in image processing

我正在尝试检测皮肤。 我找到了一个简单易用的公式来从 RGB 图片中检测皮肤。 唯一的问题是,for 循环非常慢,我需要加快这个过程。 我做了一些研究,矢量化可以加快我的 for 循环,但我不知道如何在我的案例中使用它。

这是我的 function 的代码:

Function接收1个类型参数:numpy数组,shape为(144x256x3),dtype=np.uint8

Function 返回第一个检测到的肤色像素的坐标(如 numpy.array [height,width]); 第一张皮肤检测图片(float)的皮肤检测像素数(int)和计算角度(从左到右)

# picture = npumpy array, with 144x256x3 shape, dtype=np.uint8
def filter_image(picture):
    r = 0.0
    g = 0.0
    b = 0.0

    # In first_point I save first occurrence of skin colored pixel, so I can track person movement
    first_point = np.array([-1,-1])

    # counter is used to count how many skin colored pixels are in an image (to determine distance to target, because LIDAR isn't working)
    counter = 0

    # angle of first pixel with skin color (from left to right, calculated with Horizontal FOV)
    angle = 0.0

    H = picture.shape[0]
    W = picture.shape[1]

    # loop through each pixel
    for i in range(H):
        for j in range(W):
            # if all RGB are 0(black), we take with next pixel
            if(int(picture[i,j][0]+picture[i,j][1]+picture[i,j][2])) == 0:
               continue
            #else we calculate r,g,b used for skin recognition
            else:    
                r = picture[i,j][0]/(int(picture[i,j][0]+picture[i,j][1]+picture[i,j][2]))
                g = picture[i,j][1]/(int(picture[i,j][0]+picture[i,j][1]+picture[i,j][2]))
                b = picture[i,j][2]/(int(picture[i,j][0]+picture[i,j][1]+picture[i,j][2]))
            # if one of r,g,b calculations are 0, we take next pixel
            if(g == 0 or r == 0 or b == 0):
                continue
            # if True, pixel is skin colored
            elif(r/g > 1.185 and (((r * b) / math.pow(r + b + g,2)) > 0.107) and ((r * g) / math.pow(r + b + g,2)) > 0.112):
                # if this is the first point with skin colors in the whole image, we save i,j coordinate
                if(first_point[0] == -1):
                    # save first skin color occurrence
                    first_point[0] = i
                    first_point[1] = j

                    # here angle is calculated, with width skin pixel coordinate, Hor. FOV of camera and constant
                    angle = (j+1)*91 *0.00390626

                # whenever we detect skin colored pixel, we increment the counter value
                counter += 1
                continue
    # funtion returns coordinates of first skin colored pixel, counter of skin colored pixels and calculated angle(from left to right based on j coordinate of first pixel with skin color)         
    return first_point,counter, angle

Function很好用,唯一的问题是它的速度!

谢谢你的帮忙!

在尝试提高代码性能时,首先尝试的一件事通常是很好的,那就是看看像numba这样的东西可以基本上免费地提高多少速度。

以下是如何将它用于您的代码的示例:

import math
import time

# I'm just importing numpy here so I can make a random input of the
# same dimensions that you mention in your question.
import numpy as np
from numba import jit

@jit(nopython=True)
def filter_image(picture):
    ... I just copied the body of this function from your post above ...
    return first_point, counter, angle

def main():
    n_iterations = 10
    img = np.random.rand(144, 256, 3)
    before = time.time()
    for _ in range(n_iterations):
        # In Python 3, this was just a way I could get access to the original
        # function you defined, without having to make a separate function for
        # it (as the numba call replaces it with an optimized version).
        # It's equivalent to just calling your original function here.
        filter_image.__wrapped__(img)
    print(f'took: {time.time() - before:.3f} without numba')

    before = time.time()
    for _ in range(n_iterations):
        filter_image(img)
    print(f'took: {time.time() - before:.3f} WITH numba')

if __name__ == '__main__':
    main()

Output 显示时差:

took: 1.768 without numba
took: 0.414 WITH numba

...实际上优化这个 function 可能会做得更好,但如果这种加速足以让您不需要进行其他优化,那就足够了!

编辑(根据宏观经济学家的评论):我上面报告的时间还包括numba即时编译您的 function 的前期时间成本,这是在第一次调用时发生的。 如果您多次调用此 function,性能差异实际上可能会更加显着。 在 first first 之后对所有调用进行计时应该可以使每次调用时间的比较更加准确。

您可以跳过所有循环并使用 numpy 的广播进行操作。 如果将图像从 3D 重塑为 2D,则该过程会变得更加容易,从而为您提供 HxW 像素行。

def filter(picture):
    H,W = picture.shape[0],picture.shape[1]
    picture = picture.astype('float').reshape(-1,3)
    # A pixel with any r,g,b equalling zero can be removed.
    picture[np.prod(picture,axis=1)==0] = 0

    # Divide non-zero pixels by their rgb sum
    picsum = picture.sum(axis=1)
    nz_idx = picsum!=0
    picture[nz_idx] /= (picsum[nz_idx].reshape(-1,1))

    nonzeros = picture[nz_idx]

    # Condition 1: r/g > 1.185
    C1 = (nonzeros[:,0]/nonzeros[:,1]) > 1.185
    # Condition 2: r*b / (r+g+b)^2 > 0.107
    C2 = (nonzeros[:,0]*nonzeros[:,2])/(nonzeros.sum(axis=1)**2) > 0.107 
    # Condition 3: r*g / (r+g+b)^2 > 0.112
    C3 = (nonzeros[:,0]*nonzeros[:,1])/(nonzeros.sum(axis=1)**2) > 0.112
    # Combine conditions
    C = ((C1*C2*C3)!=0)
    picsum[nz_idx] = C
    skin_points = np.where(picsum!=0)[0]
    first_point = np.unravel_index(skin_points[0],(H,W))
    counter = len(skin_points)
    angle = (first_point[1]+1) * 91 * 0.00390626
    return first_point, counter, angle

暂无
暂无

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

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