利用机器学习进行皮肤分割和主色调/颜色提取
今天我们将学习使用OpenCV来分割皮肤并使用Sci Kit学习执行K-Means聚类以找到主要的肤色。
假设您了解基本的python并了解OpenCV。我们将涵盖对K-Means和OpenCV的一些方法的高级理解。我们还将讨论不同的色彩空间。
本文的重点是解释代码背后的概念。
什么是OpenCV?

“OpenCV是一个主要针对实时计算机视觉的编程函数库”
那么什么是计算机视觉?
“计算机视觉涉及从单个图像或一系列图像中自动提取,分析和理解有用信息。它涉及开发理论和算法基础,以实现自动视觉理解。“
计算机视觉是人工智能的一个子领域,它涵盖了从简单的物体识别到生产级机器人的应用。一个示例用例,每个人都可以与Facebook建议在上传照片时标记自己或朋友。
因此,让我们使用高级解释,并为OpenCV做一个简单的定义
“OpenCV是一个计算机视觉库,可帮助我们使用图像处理技术从图像或视频中提取/分析数据”。
什么是Sci-Kit学习?

“Scikit-learn(以前称为scikits.learn)是一个用于Python编程语言的免费软件机器学习库。它具有各种分类,回归和聚类算法,包括支持向量机,随机森林,梯度增强,k-means和DBSCAN“
机器学习是人工智能的子领域,其重点是为计算机/系统提供自动从数据学习而无需明确编程的算法。它基本上意味着允许计算机在没有“if else”语句的情况下进行预测或假设。
机器学习算法包括
- 监督学习
- 无人监督的学习
- 强化学习
什么是K-Means聚类?
K-Means聚类是一种无监督学习算法。聚类算法的基本原理是它在给定数据集中找到具有相似特征的数据点组。K-Means是一种这样的聚类算法,它将数据分组为“K”组/clusters。
这个过程很简单:
- 选择clusters 数量(K)
- 随机放置K个数据点(初始质心)
- 将数据集中的点分配给最近的质心(这通常是从质心中找到点的欧几里德距离)
- 通过获取每个cluster中的数据点的平均值来重新计算新的质心。
- 重复3和4直到质心不移动。


Python编码
实现的过程:
- 1. 读取图像 - 这可以使用OpenCV完成
- 2. 从图像中分割出皮肤 - 这也可以使用OpenCV完成
- 3. 找到主导色彩 - 这是主要目标!我们将在Scikit-learn python包的帮助下使用K-Mean聚类算法。
所以我们分三个步骤。在您真正理解代码之前需要了解一些事项
1.OpenCV在“BGR Color Space”中读取彩色图像。
这与“RGB色彩空间”相反。
2.使用HSV颜色空间中的阈值处理完成皮肤分割
HSV(Hue, Saturation, Value)是用于表示与人类感知对齐的RGB颜色的模型。Hue表示特定颜色的波长优势,Saturation表示颜色的深浅,Value表示颜色的强度。
阈值处理是通过基于定义的阈值过滤掉像素来创建二值图像的过程。简单来说,取图像的每个像素,如果该像素在“阈值”范围内,如果不使其为黑色,则使其为白色。在我们的上下文中,阈值将是表示“肤色”范围的HSV值。SV的范围可以通过试错得到。
我们将使用cv2.inRange()方法进行阈值处理,并使用cv2.bitwise_and()从二进制图像中获取最终的减影图像。
还有一些其他要点可以了解要使用的库
OpenCV使用Numpy数组作为数据类型。
默认情况下,OpenCV无法处理透明图像。它将透明视为黑色
由于cv2.imshow()在Google Collab中不起作用,我们将使用matplotlib的plypot“imshow”方法来显示图像。
我们将使用PyImageSearch.com的Adrian Rosebrock作者提供的令人敬畏的“ imutils ”库。
Python代码如下:
import numpy as np
import cv2
from sklearn.cluster import KMeans
from collections import Counter
import imutils
import pprint
from matplotlib import pyplot as plt
def extractSkin(image):
# Taking a copy of the image
img = image.copy()
# Converting from BGR Colours Space to HSV
img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Defining HSV Threadholds
lower_threshold = np.array([0, 48, 80], dtype=np.uint8)
upper_threshold = np.array([20, 255, 255], dtype=np.uint8)
# Single Channel mask,denoting presence of colours in the about threshold
skinMask = cv2.inRange(img, lower_threshold, upper_threshold)
# Cleaning up mask using Gaussian Filter
skinMask = cv2.GaussianBlur(skinMask, (3, 3), 0)
# Extracting skin from the threshold mask
skin = cv2.bitwise_and(img, img, mask=skinMask)
# Return the Skin image
return cv2.cvtColor(skin, cv2.COLOR_HSV2BGR)
def removeBlack(estimator_labels, estimator_cluster):
# Check for black
hasBlack = False
# Get the total number of occurance for each color
occurance_counter = Counter(estimator_labels)
# Quick lambda function to compare to lists
def compare(x, y): return Counter(x) == Counter(y)
# Loop through the most common occuring color
for x in occurance_counter.most_common(len(estimator_cluster)):
# Quick List comprehension to convert each of RBG Numbers to int
color = [int(i) for i in estimator_cluster[x[0]].tolist()]
# Check if the color is [0,0,0] that if it is black
if compare(color, [0, 0, 0]) == True:
# delete the occurance
del occurance_counter[x[0]]
# remove the cluster
hasBlack = True
estimator_cluster = np.delete(estimator_cluster, x[0], 0)
break
return (occurance_counter, estimator_cluster, hasBlack)
def getColorInformation(estimator_labels, estimator_cluster, hasThresholding=False):
# Variable to keep count of the occurance of each color predicted
occurance_counter = None
# Output list variable to return
colorInformation = []
# Check for Black
hasBlack = False
# If a mask has be applied, remove th black
if hasThresholding == True:
(occurance, cluster, black) = removeBlack(
estimator_labels, estimator_cluster)
occurance_counter = occurance
estimator_cluster = cluster
hasBlack = black
else:
occurance_counter = Counter(estimator_labels)
# Get the total sum of all the predicted occurances
totalOccurance = sum(occurance_counter.values())
# Loop through all the predicted colors
for x in occurance_counter.most_common(len(estimator_cluster)):
index = (int(x[0]))
# Quick fix for index out of bound when there is no threshold
index = (index-1) if ((hasThresholding & hasBlack)
& (int(index) != 0)) else index
# Get the color number into a list
color = estimator_cluster[index].tolist()
# Get the percentage of each color
color_percentage = (x[1]/totalOccurance)
# make the dictionay of the information
colorInfo = {"cluster_index": index, "color": color,
"color_percentage": color_percentage}
# Add the dictionary to the list
colorInformation.append(colorInfo)
return colorInformation
def extractDominantColor(image, number_of_colors=5, hasThresholding=False):
# Quick Fix Increase cluster counter to neglect the black(Read Article)
if hasThresholding == True:
number_of_colors += 1
# Taking Copy of the image
img = image.copy()
# Convert Image into RGB Colours Space
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Reshape Image
img = img.reshape((img.shape[0]*img.shape[1]), 3)
# Initiate KMeans Object
estimator = KMeans(n_clusters=number_of_colors, random_state=0)
# Fit the image
estimator.fit(img)
# Get Colour Information
colorInformation = getColorInformation(
estimator.labels_, estimator.cluster_centers_, hasThresholding)
return colorInformation
def plotColorBar(colorInformation):
# Create a 500x100 black image
color_bar = np.zeros((100, 500, 3), dtype="uint8")
top_x = 0
for x in colorInformation:
bottom_x = top_x + (x["color_percentage"] * color_bar.shape[1])
color = tuple(map(int, (x['color'])))
cv2.rectangle(color_bar, (int(top_x), 0),
(int(bottom_x), color_bar.shape[0]), color, -1)
top_x = bottom_x
return color_bar
"""## Section Two.4.2 : Putting it All together: Pretty Print
The function makes print out the color information in a readable manner
"""
def prety_print_data(color_info):
for x in color_info:
print(pprint.pformat(x))
print()
"""
The below lines of code, is the implementation of the above defined function.
"""
'''
Skin Image Primary : https://raw.githubusercontent.com/octalpixel/Skin-Extraction-from-Image-and-Finding-Dominant-Color/master/82764696-open-palm-hand-gesture-of-male-hand_image_from_123rf.com.jpg
Skin Image One : https://raw.githubusercontent.com/octalpixel/Skin-Extraction-from-Image-and-Finding-Dominant-Color/master/skin.jpg
Skin Image Two : https://raw.githubusercontent.com/octalpixel/Skin-Extraction-from-Image-and-Finding-Dominant-Color/master/skin_2.jpg
Skin Image Three : https://raw.githubusercontent.com/octalpixel/Skin-Extraction-from-Image-and-Finding-Dominant-Color/master/Human-Hands-Front-Back-Image-From-Wikipedia.jpg
'''
# Get Image from URL. If you want to upload an image file and use that comment the below code and replace with image=cv2.imread("FILE_NAME")
image = imutils.url_to_image(
"https://raw.githubusercontent.com/octalpixel/Skin-Extraction-from-Image-and-Finding-Dominant-Color/master/82764696-open-palm-hand-gesture-of-male-hand_image_from_123rf.com.jpg")
# Resize image to a width of 250
image = imutils.resize(image, width=250)
# Show image
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.show()
# Apply Skin Mask
skin = extractSkin(image)
plt.imshow(cv2.cvtColor(skin, cv2.COLOR_BGR2RGB))
plt.show()
# Find the dominant color. Default is 1 , pass the parameter 'number_of_colors=N' where N is the specified number of colors
dominantColors = extractDominantColor(skin, hasThresholding=True)
# Show in the dominant color information
print("Color Information")
prety_print_data(dominantColors)
# Show in the dominant color as bar
print("Color Bar")
colour_bar = plotColorBar(dominantColors)
plt.axis("off")
plt.imshow(colour_bar)
plt.show()这将是代码的最终输出,将颜色信息和颜色条如下所示。
