[英]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.