Aprendizaje automático: KNN completa la clasificación del lenguaje de señas en inglés
Primero echemos un vistazo al lenguaje de señas inglés estándar:
En este documento, la red KNN reconoce parte del efecto: (por supuesto, solo se seleccionan algunas letras):
El proceso principal completado es:
- Procese un conjunto de datos de imagen en kaggle a través de mediapipe para obtener la relación posicional de 21 puntos de referencia
- Construir un modelo KNN
- control de modelo
Construir un conjunto de datos
El conjunto de datos de la imagen proviene de un conjunto de datos en Kaggle, el enlace del conjunto de datos .
Una vez que se completa la descarga, obtenemos un montón de imágenes de 128 * 128. Aquellos que no conocen DL solo pueden usar mediapipe para completar la extracción de información de puntos de referencia (los paquetes relacionados con mediapipe se presentan en otro artículo , y también puede ver el final del artículo).
El flujo de procesamiento es:
- Atraviese el conjunto de datos de imagen y use mediapipe para completar la extracción de información de gestos (para mejorar la precisión, se puede ajustar el intervalo de confianza, que se presenta en otro artículo), es decir, las coordenadas xy de 21 puntos de coordenadas.
- El proveedor del conjunto de datos establece la última letra del archivo de imagen como el significado del gesto, y podemos guardarlo en el archivo como nuestro valor objetivo.
import pandas as pd
import HandTrackingModule as htm
import cv2
import os
import time
import numpy as np
import csv
detector = htm.handDetctor(mode=True, detectionCon=0.6, trackCon=0.6)
csv_col_name = ['0_x', '0_y', '1_x', '1_y', '2_x', '2_y', '3_x', '3_y', '4_x', '4_y', '5_x', '5_y',
'6_x', '6_y', '7_x', '7_y', '8_x', '8_y', '9_x', '9_y', '10_x', '10_y', '11_x', '11_y',
'12_x', '12_y', '13_x', '13_y', '14_x', '14_y', '15_x', '15_y', '16_x', '16_y', '17_x', '17_y',
'18_x', '18_y', '19_x', '19_y', '20_x', '20_y','target']
def load_image():
path = "dataset5"
dirs = os.listdir(path)
for file_ABCD in dirs:
for file_abcd in os.listdir(path+"/"+file_ABCD):
for img_path in os.listdir(path+"/"+file_ABCD+"/"+file_abcd):
# 跳过暗的图片
if "depth" in img_path:
continue
# print(path+"/"+file_ABCD+"/"+file_abcd+"/"+img_path)
img = cv2.imread(path+"/"+file_ABCD+"/"+file_abcd+"/"+img_path)
# 对图像进行处理
img = detector.findHands(img)
lmList = detector.findPosition(img, draw=False)
if len(lmList) == 42:
# print(lmList)
# print(file_abcd)
lmList.append(file_abcd)
# 将特征点写入csv文件中
write_to_csv(lmList)
cv2.imshow("show", img)
cv2.waitKey(1)
def write_to_csv(lmList):
# test = pd.DataFrame(columns=csv_col_name, data=[lmList])
# test.to_csv('testcsv.csv', index=True)
with open(r'testcsv.csv', mode='a', newline='', encoding='utf8') as cfa:
csv_write = csv.writer(cfa)
csv_write.writerow(lmList)
return None
if __name__ == '__main__':
load_image()
Guardamos los datos en el archivo testcsv.csv , como se muestra a continuación:
De hecho, la cantidad de datos es un poco pequeña.
Construir modelos y evaluar
Modelado:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
import joblib
def load_data():
data = pd.read_csv("testcsv.csv")
# print(data.iloc[:, 0:42])
# 划分数据集
x_train, x_test, y_train, y_test = train_test_split(data.iloc[:, 0:42], data.target, random_state=10)
# 标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
# 训练集和测试集做相同处理(很重要!)
x_test = transfer.transform(x_test)
# KNN算法预估器 建立模型
estimator = KNeighborsClassifier(n_neighbors=10)
# 添加网格搜索交叉验证
param_dict = {
"n_neighbors": [11, 13, 15, 17, 19, 21]}
estimator = GridSearchCV(estimator, param_grid=param_dict, cv=10)
estimator.fit(x_train, y_train)
# 模型评估
# 1 直接对比真实值和预估值
y_predict = estimator.predict(x_test)
print(y_predict == y_test)
# 计算准确率
score = estimator.score(x_test, y_test)
print(score)
# 保存模型
joblib.dump(estimator, "k_near.pkl")
Detección en tiempo real:
import HandTrackingModule as htm
import cv2
import numpy as np
wCam, hCam = 640, 480
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
cap.set(3, wCam)
cap.set(4, hCam)
detector = htm.handDetctor(detectionCon=0.6, trackCon=0.6)
model = joblib.load("k_near.pkl")
while True:
success, img = cap.read()
img = detector.findHands(img)
lmList = detector.findPosition(img, draw=False)
if len(lmList) == 42:
lm = transfer.transform(np.array(lmList).reshape(1, -1))
m_predict = model.predict(lm)
cv2.putText(img, str(m_predict), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 255), 3)
cv2.imshow("image", img)
if cv2.waitKey(2) & 0xFF == 27:
break
punto optimizable
- Los parámetros de KNN y el rango de búsqueda de cuadrícula se pueden optimizar aún más.
- KNN en sí solo es adecuado para escenarios de datos pequeños y es sensible al valor K, y el efecto general de reconocimiento de gestos es promedio.
- La parte de preprocesamiento de datos es extremadamente tosca y se pueden realizar operaciones como la limpieza de datos.
He estudiado, pero me da pereza hacerlo.
adjunto
HandTrackingModule.py
import cv2
import mediapipe as mp
import time
import math
class handDetctor():
def __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5):
self.mode = mode
self.maxHands = maxHands
self.detectionCon = detectionCon
self.trackCon = trackCon
self.mpHands = mp.solutions.hands
self.hands = self.mpHands.Hands(self.mode, self.maxHands,
self.detectionCon, self.trackCon)
self.mpDraw = mp.solutions.drawing_utils
def findHands(self, img, draw=True, ):
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)#转换为rgb
self.results = self.hands.process(imgRGB)
# print(results.multi_hand_landmarks)
if self.results.multi_hand_landmarks:
for handLms in self.results.multi_hand_landmarks:
if draw:
self.mpDraw.draw_landmarks(img, handLms, self.mpHands.HAND_CONNECTIONS)
return img
def findPosition(self, img, handNo=0, draw=True):
lmList = []
if self.results.multi_hand_landmarks:
myHand = self.results.multi_hand_landmarks[handNo]
for id, lm in enumerate(myHand.landmark):
# print(id, lm)
# 获取手指关节点
h, w, c = img.shape
# cx, cy = int(lm.x*w), int(lm.y*h)
lmList.append(lm.x)
lmList.append(lm.y)
# if draw:
# cv2.putText(img, str(int(id)), (cx+10, cy+10), cv2.FONT_HERSHEY_PLAIN,
# 1, (0, 0, 255), 2)
return lmList
def fingerStatus(self, lmList):
# 返回列表 包含每个手指的开合状态
fingerList = []
id, originx, originy = lmList[0]
keypoint_list = [[2, 4], [6, 8], [10, 12], [14, 16], [18, 20]]
for point in keypoint_list:
id, x1, y1 = lmList[point[0]]
id, x2, y2 = lmList[point[1]]
if math.hypot(x2-originx, y2-originy) > math.hypot(x1-originx, y1-originy):
fingerList.append(True)
else:
fingerList.append(False)
return fingerList