使用Python在OpenCV中为肤色检测选择复杂的颜色范围

时间:2015-03-06 12:34:15

标签: python opencv numpy computer-vision image-recognition

我正在尝试制作肤色检测程序。基本上,它从webcamera获取视频,然后创建一个掩码,之后只有皮肤应该可见。我在一篇论文中找到了a criterium for detecting skin-colour ranges。它看起来像这样:

  

均匀日光照明规则中的肤色定义为   (R> 95)AND(G> 40)AND(B> 20)AND(max {R,G,B} - min {R,G,B}   15)AND(| R-G |> 15)AND(R> G)AND(R> B)(1)同时给出手电筒或日光侧向照射规则下的肤色   通过(R> 220)和(G> 210)和(B> 170)AND(| R-G |< = 15)和   (R> B)AND(G> B)

我在Python中所做的是:

def check(list):
return ( ( (list[2]>95) and (list[1]>40) and (list[0]>20)) and ((max(list)-min(list))>15)       
        and (abs(list[2]-list[1])>15) and (list[2]>list[1]) and (list[2]>list[0]))

def check2(list):
    return (list[2]>220) and (list[1]>210) and (list[0]>170) and (abs(list[2]-list[1])<=15) and ((list[2]>list[0]) and (list[1]>list[0]))

(grabbed, frame) = camera.read()
img=frame   
img=img.tolist()
skinmask =  [[(1 if (check(list) or check2(list)) else 0) for list in l1] for l1 in img]
mask=np.array(skinmask, dtype = "uint8")
skin = cv2.bitwise_and(frame, frame, mask = mask)
cv2.imshow("images", np.hstack([frame, skin]))

但它不是,我真正期待的。它减缓了这个过程。我发现cv2.inRange(image, lower, upper)但它无法处理如此复杂的颜色范围规则。 有没有其他方法以更有效的方式做到这一点?

1 个答案:

答案 0 :(得分:2)

这里的瓶颈在于您将frame返回的numpy数组camera.read()(即相机拍摄的快照)转换为普通的Python列表。然后,您将使用普通的for循环迭代元素,相比之下这些循环相对较慢。

你应该做的是使用numpy的矢量化操作来缩短执行时间。

您的示例,略微重写(list - &gt; px),为了清晰起见而针对颜色通道进行了更正(红色像素位于px[0],不是px[2]):

import cv2
import numpy as np

camera = cv2.VideoCapture(0)
(grabbed, frame) = camera.read()

def check(px):
    R, G, B = px
    return ( ((R > 95) and (G > 40) and (B > 20))
        and ((max(px)-min(px))>15) and (abs(R - G) > 15) and
        (R > G) and (R > B))

def check2(px):
    R, G, B = px
    return ((R >220) and (G > 210) and (B > 170) and
        (abs(R - G) <= 15) and (R > B) and (G > B))

def iterate_over_list(img):  # your method
    img = img.tolist()
    skinmask =  [[(1 if (check(px) or check2(px)) else 0) for px in row] for row in img]
    return skinmask

这可以用矢量化形式重写为:

def vectorized_form(img):
    R,G,B = [img[:,:,x] for x in range(3)]
    delta15 = np.abs(R.astype(np.int8) - G.astype(np.int8)) > 15  # watch out for np.abs(R-G): because of the UNsigned numbers, they could get clipped!
    more_R_than_B = (R > B)
    is_skin_coloured_during_daytime = ((R > 95) & (G > 40) & (B > 20) &
        (img.ptp(axis=-1) > 15) & delta15 & (R > G) & more_R_than_B)
    is_skin_coloured_under_flashlight = ((R > 220) & (G > 210) & (B > 170) &
        ~delta15 & more_R_than_B & (G > B))
    return np.logical_or(is_skin_coloured_during_daytime, is_skin_coloured_under_flashlight)

请注意,您可以删除至少一个逻辑和操作:more_R_than_B出现在每个检查中,然后使用逻辑或操作。实际上表格语法为:(A & B) | (C & B) == (A | C) & B。但现在我进行了微观优化,我想保留原始形式,因为它会显示你引用的纸张的1对1地图。

我系统的时间考虑因素显示速度增加了约19倍。备注,我的测试图像有(480, 640, 3)形状。对于较大的图像,速度增加会大得多,因为在您的方法中,您使用标准的Python for循环迭代像素,而我只是使用向量化的例程。

In [27]: %timeit iterate_over_list(frame)
1 loops, best of 3: 321 ms per loop

In [28]: %timeit vectorized(frame)
100 loops, best of 3: 16.8 ms per loop