Select 使用 Python 在 OpenCV 中进行肤色检测的复杂颜色范围
Select complex colour range for skin-colour detection in OpenCV with Python
我正在尝试制作肤色检测程序。基本上,它从网络摄像头获取视频,然后创建一个遮罩,之后只有皮肤应该可见。我在一篇论文中找到了 a criterium for detecting skin-colour ranges。它看起来像这样:
The skin colour at uniform daylight illumination rule is defined as
(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) while the skin colour under flashlight or daylight lateral illumination rule is given
by (R > 220 ) AND (G > 210 ) AND (B > 170 ) AND (|R - G| <= 15 ) AND
(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)
但它无法处理如此复杂的颜色范围规则。
有没有其他方法可以更有效地做到这一点?
这里的瓶颈在于,您正在将 camera.read()
(即相机拍摄的快照)返回的 numpy 数组 frame
转换为普通的 Python 列表。然后,您将使用普通的 for 循环遍历元素,相比之下,这种循环相对较慢。
您应该做的是使用 numpy 的矢量化操作来缩短执行时间。
您的示例,为了清晰起见,稍微重写了 (list
-> 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
出现在每个检查中,然后使用 逻辑或操作。事实上 table 语法:(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
我正在尝试制作肤色检测程序。基本上,它从网络摄像头获取视频,然后创建一个遮罩,之后只有皮肤应该可见。我在一篇论文中找到了 a criterium for detecting skin-colour ranges。它看起来像这样:
The skin colour at uniform daylight illumination rule is defined as (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) while the skin colour under flashlight or daylight lateral illumination rule is given by (R > 220 ) AND (G > 210 ) AND (B > 170 ) AND (|R - G| <= 15 ) AND (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)
但它无法处理如此复杂的颜色范围规则。
有没有其他方法可以更有效地做到这一点?
这里的瓶颈在于,您正在将 camera.read()
(即相机拍摄的快照)返回的 numpy 数组 frame
转换为普通的 Python 列表。然后,您将使用普通的 for 循环遍历元素,相比之下,这种循环相对较慢。
您应该做的是使用 numpy 的矢量化操作来缩短执行时间。
您的示例,为了清晰起见,稍微重写了 (list
-> 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
出现在每个检查中,然后使用 逻辑或操作。事实上 table 语法:(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